-
Pythonic
명시적이고, 단순하고, 가독성이 좋은것.
Easy to read, Don't Repeat yourselfHow 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 = "아무것도 없음"
'💫 Computer Science > Python & AI Framework' 카테고리의 다른 글
[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