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 = "아무것도 없음"
'AI 개발' 카테고리의 다른 글
[Docker] ML + NLP Dockerfile 만들기 (0) | 2021.05.11 |
---|---|
[Python] 인코딩 정리 (0) | 2021.04.29 |
[Python] Effective Python 1 - call by assignment (0) | 2021.02.20 |
[Python] Class & Underscore( _ , __ ) (7) | 2020.12.18 |
[Python] list, tuple, dictionary, iterator(map, filter) (0) | 2020.09.06 |
Contents
소중한 공감 감사합니다