새소식

AI 개발

[Python] Effective Python 2 - Pythonic

  • -

 

 

 

Pythonic

 

명시적이고, 단순하고, 가독성이 좋은것.

Easy to read, Don't Repeat yourself

 

 

How to make pythonic code

 

1. 한줄로 굳이 작성해도 되지 않아도 될 것을 한줄로 작성해서 시각적 잡음을 일으키지 말자.

from urllib.parse import parse_qs

my_values = parse_qs("빨강=5&초록=0&파랑=", keep_blank_values=True)

red = my_values.get("빨강", [""])[0] or 0
print(f"빨강: {red!r}")
green = my_values.get("초록", [""])[0] or 0
print(f"초록: {green!r}")
blue = my_values.get("파랑", [""])[0] or 0
print(f"파랑: {blue!r}")

# 값을 int로 바꿔야할 때
red = int(my_values.get("빨강", [""])[0] or 0)
# 복잡한 식을 한줄로 표현 X
red_str = my_values.get("빨강", [""])[0] or 0
red = int(red_str[0]) if red_str[0] else 0
# 여러 줄로 나누어쓴 if else 문이 더 정확하다
red_str = my_values.get("빨강", [""])[0] or 0
if red_str[0]:
    red = int(red_str[0])
else:
    red = 0

 

 

2. or 이나 and 식을 사용하는 것 보단 if/else를 사용해라.

 

 

3. 되도록 if/else 문을 여러줄로 작성해서 명확하게 만들어주어야 한다.

 

 

4. 단지 두 세번 반복되는 로직일지라도 도우미 함수(help function)를 작성하자.

# 도우미 함수를 쓰자 단지 2,3 번 반복에 불과하더라도
def get_first_int(values, key, default=0):
    found = values.get(key, [""])
    if found[0]:
        return int(found[0])
    return default

 

 

5. 빈 문자열, 빈 리스트, 0 모두 암시적으로 False가 된다는 것을 이용해라.

  • len대신 if not 을 사용
  • 딕셔너리 키가 없을 경우 KeyError 말고 get을 사용.
from urllib.parse import parse_qs

my_values = parse_qs("빨강=5&초록=0&파랑=", keep_blank_values=True)

print(my_values)  # {'빨강': ['5'], '초록': ['0'], '파랑': ['']}

# 값이 비어있거나 없는 경우 0으로 default 생성해주기

# dic.get(key, default) key가 없는 경우 default로 저장
# or -> False면 좌 값 아니면 우 값
# 빈 문자열, 리스트, 0 모두 암시적으로 False 취급.
red = my_values.get("빨강", [""])[0] or 0
print(f"빨강: {red!r}")
green = my_values.get("초록", [""])[0] or 0
print(f"초록: {green!r}")
blue = my_values.get("파랑", [""])[0] or 0
print(f"파랑: {blue!r}")

 

 

6. 절대로 for, while 뒤에 else를 사용하지 말 것.

# 서로소 검사
# for/while 뒤에 else 쓰지 X
a, b = 4, 9
for i in range(2, min(a, b) + 1):
    if a % i == 0 and b % i == 0:
        print("서로소 아님")
        break  # else 실행되지 않음
else:
    print("서로소임")

# 1. 도우미 함수를 작성해 바로 리턴
def coprime(a, b):
    for i in range(2, min(a, b) + 1):
        if a % i == 0 and b % i == 0:
            return False
    return True


assert coprime(a, b)
assert not coprime(3, 6)

# 2. 도우미 함수를 작성해 변수 리턴
def coprime_alternate(a, b):
    is_coprime = True
    for i in range(2, min(a, b) + 1):
        if a % i == 0 and b % i == 0:
            is_coprime = False
            break
    return is_coprime


assert coprime_alternate(a, b)
assert not coprime_alternate(3, 6)

 

 

7. 대입식(:=)을 사용해 반복을 피하기 - walrus operator(assignment expression)

python 3.8이상만 가능

# 0인지 검사하는 패턴 파이썬에서 자주 사용
def make_lemonade(count):
    pass


def out_of_stock():
    pass


fresh_fruit = {"사과": 10, "배": 9, "바나나": 6}
count = fresh_fruit.get("레몬", 0)
if count:
    make_lemonade(count)
else:
    out_of_stock()

# walrus operator = assignment expression 대입식
# count가 if에만 영향을 준다는 것을 파악하기 쉽다.
# 대입식은 대입문이 쓰일 수 없는 곳에도 쓸 수 있게 해줌.
if count := fresh_fruit.get("레몬", 0):
    make_lemonade(count)
else:
    out_of_stock()

# 레몬은 0인지 아닌지만 검사하면 된다
# 사과일 경우 4개 이상이 있는지 검사해야한다면
if (count := fresh_fruit.get("사과", 0)) > 4:
    make_lemonade(count)
else:
    out_of_stock


## 바나나 스무디 바나나2개 필요하고 없을 경우 OutOfBananas 발생
def slice_banana(count):
    return count * 3


class OutOfBananas(Exception):
    pass


def make_smooth(count):
    return "smoothie"


pieces = 0
count = fresh_fruit.get("바나나", 0)
if count >= 2:
    pieces = slice_banana(count)

try:
    smoothies = make_smooth(pieces)
except OutOfBananas:
    out_of_stock()

# walrus
pieces = 0
if (count := fresh_fruit.get("바나나", 0)) >= 2:
    pieces = slice_banana(count)

try:
    smoothies = make_smooth(pieces)
except OutOfBananas:
    out_of_stock()

 

 

8. do while과 switch 도 walrus를 사용해서 구현할 수 있다.

# do while 없음
import random


def pick_fruit():
    if random.randint(1, 10) > 2:  # 80% 확률로 새 과일 보충
        return {
            "사과": random.randint(0, 10),
            "바나나": random.randint(0, 10),
            "레몬": random.randint(0, 10),
        }
    else:
        return None


def make_juice(fruit, count):
    if fruit == "사과":
        return [("사과주스", count / 4)]
    elif fruit == "바나나":
        return [("바나나스무디", count / 2)]
    elif fruit == "레몬":
        return [("레모네이드", count / 1)]
    else:
        return []


# 초기화하면서 pick_fruit() 호출, 마지막에 pick_fruit() 호출 2번 호출됨.
bottles = []
fresh_fruit = pick_fruit()
while fresh_fruit:
    for fruit, count in fresh_fruit.items():
        batch = make_juice(fruit, count)
        bottles.extend(batch)
    fresh_fruit = pick_fruit()

print(bottles)

# 무한 루프는 제어가 모두 break에 달려있어버림
bottles = []
while True:  # 무한루프
    fresh_fruit = pick_fruit()
    if not fresh_fruit:  # 중간에서 끝내기
        break

    for fruit, count in fresh_fruit.items():
        batch = make_juice(fruit, count)
        bottles.extend(batch)

print(bottles)

# walrus로 매번 fresh_fruit에 대입하고 검사할 수 있다.
bottles = []
while fresh_fruit := pick_fruit():
    for fruit, count in fresh_fruit.items():
        batch = make_juice(fruit, count)
        bottles.extend(batch)

print(bottles)

 

 

# 가장 좋은 주스 먼저 제공하고, 바나나, 키위, 수박 제공
fresh_fruit = {
    "사과": 10,
    "바나나": 8,
    "레몬": 5,
}


def make_lemonade(count):
    n = 1
    print(f"레몬 {count*n} 개로 레모네이드 {count//n} 개를 만듭니다.")
    fresh_fruit["레몬"] -= count * n
    return f'레몬이 {fresh_fruit["레몬"]} 개 남았습니다.'


def out_of_stock():
    print(f"제료가 부족합니다. 재료를 보충해 주세요.")


def make_cider(count):
    n = 4

    print(f"사과 {count} 개로 사과주스 {count//n} 개를 만듭니다.")
    fresh_fruit["사과"] -= n * (count // n)
    return f'사과가 {fresh_fruit["사과"]} 개 남았습니다.'


def slice_bananas(count):
    print(f"바나나 {count} 개를 슬라이스합니다.")
    fresh_fruit["바나나"] -= count
    return count


class OutOfBananas(Exception):
    pass


def make_smoothies(count):
    n = 2
    if count > n:
        print(f"바나나 슬라이스 {count} 개로 스무디 {count//n} 개를 만듭니다.")
        return f'바나나가 {fresh_fruit["바나나"]} 개 남았습니다.'
    else:
        raise OutOfBananas


########### switch를 흉내내려면 if else elif를 깊게 내포시키는 것이다.
# 좋지 않다.
count = fresh_fruit.get("banana", 0)

if count >= 2:
    pieces = slice_bananas(count)
    to_enjoy = make_smoothies(pieces)
else:
    count = fresh_fruit.get("사과", 0)
    if count >= 4:
        to_enjoy = make_cider(count)
    else:
        count = fresh_fruit.get("레몬", 0)
        if count:
            to_enjoy = make_lemonade(count)
        else:
            to_enjoy = "아무것도 없음"

# walrus를 이용하자
if (count := fresh_fruit.get("바나나", 0)) >= 2:
    pieces = slice_bananas(count)
    to_enjoy = make_smoothies(pieces)
elif (count := fresh_fruit.get("사과", 0)) >= 4:
    to_enjoy = make_cider(count)
elif count := fresh_fruit.get("레몬", 0):
    to_enjoy = make_lemonade(count)
else:
    to_enjoy = "아무것도 없음"
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.