Search
📘

Effective Python 2/E - Brett Slatkin

1. Pythonic

가. Helper Function > Complex Expressions

복잡한 식 보다는 도우미 함수를 사용하여 가독성을 높일 것
Don’t Do This
request = {"name": ["MJ"], "phone": ["010-111"]} name = request.get("name", [""])[0] or 0
Python
복사
Do this
def get_first_value(values, key, default=0): found = values.get(key, ['']) if found[0]: return int(found[0]) return default request = {"name": ["MJ"], "phone": ["010-111"]} name = get_first_value(request, "name")
Python
복사
Why
명확함에 따른 가독성 우위가 간결함에 따른 미세한 성능 우위 보다 낫다
→ 가독성 우위는 유지보수의 편리함으로 이어지고 이것은 휴먼에러 감소와 유지보수 용이로 이어짐
→ 미세한 성능 우위는 사람이 체감할 수 없음

나. unpacking > indexing

반복문에서 index 대신 unpacking을 사용하여 가독성을 높일 것
Don’t Do This
name_age_tuples = [('MJ', 30), ('DB', 29)] for i in range(len(name_age_tuples)): name_age_tuple = name_age_tuples[i] name = name_age_tuple[0] age = name_age_tuple[1] print(f'{i + 1}: {name}, {age}')
Python
복사
Do this
특히 반복문에서 unpacking 사용 시 코드의 가독성이 크게 향상된다
name_age_tuples = [('MJ', 30), ('DB', 29)] for i, (name, age) in enumerate(name_age_tuples, 1): print(f'{i}: {name}, {age}')
Python
복사
Why
인덱스를 사용하지 않으면 ‘시각적 잡음'이 줄기 때문에 코드의 의도가 명확하게 드러난다

다. zip for iterators in parallel

길이가 같은 복수개의 이터레이터 사용 시 zip을 사용하여 가독성을 높일 것
Don’t Do This
longest_name = None max_count = 0 for i in rage(len(names)): count = counts[i] if count > max_count: longest_name = names[i] max_count = count
Python
복사
Do this
길이가 다를 경우, 짧은 길이의 이터레이터에 맞춰 반복됨
→ 긴 길이의 이터레이터에 맞춰 반복할 때는 zip 대신 itertools.zip_longest 함수 사용
→ 이 때, 짧은 길이의 이터레이터의 outbound 요소는 None 처리됨
for count, name zip(counts, names): if count > max_count: longest_name = name max_count = count
Python
복사
Why
인덱스를 사용하지 않으면 ‘시각적 잡음'이 줄기 때문에 코드의 의도가 명확하게 드러난다

라. assignment expression

왈러스 연산자를 사용하여 메소드 호출과 변수 할당 식의 반복을 줄일 것
Don’t Do This
Python
복사
Do this
Python
복사
Why

2. List & Dict

가. unpacking > slicing

source interator 반복 사용 시 unpakcing 활용하여 코드반복과 휴먼에러 가능성을 줄일 것
Don’t Do This
all_csv_rows = list(generate_csv(source)) header = all_csv_rows[0] rows = all_csv_rows[1:] print(f'header: {header}, len of rows: {len(rows)}')
Python
복사
Do this
단, unpacking에 따른 결과를 메모리가 충분히 감당할 수 있는 경우만 사용한다
→ 메모리 부족에 따른 프로그램 강제 종료로 이어질 수 있음
만약 rows에 많은 데이터가 들어있을 가능성이 있다면 예외나 유효성 검사를 통해 가능성을 사전 차단해야 한다
→ 예를 들어 csv파일 여부 및 파일 이름 등을 체크하여 의도한 csv 파일만 받을 수 있도록 유효성 로직을 추가한다
it = generate_csv(source) header, *rows = it print(f'header: {header}, len of rows: {len(rows)}')
Python
복사
Why
unpacking 사용 시 index에 기반한 slicing 사용 보다 휴먼에러를 줄이는데 용이하다
unpacking 사용에 따라 반복을 줄이고 인덱스를 사용하지 않음으로써 코드의 의도가 명확하게 드러난다

나. get > in, KeyError

Don’t Do This
Python
복사
Do this
Python
복사
Why

다. defaultdict > setdefault

Don’t Do This
Python
복사
Do this
Python
복사
Why

라. __missing__ for setting default

Don’t Do This
Python
복사
Do this
Python
복사
Why

3. Function

가. Exception > None

예외 발생 시 None을 반환하지 말고 Exception을 발생시켜 휴먼에러를 줄일 것
Don’t Do This
unpacking에 따른 None 반환도 자제
def divide(a, b): try: return a / b except ZeroDivisionError: return None
Python
복사
def divide(a, b): try: return True, a / b except ZeroDivisionError: return False, None
Python
복사
Do this
Exception 처리 부분이 있다면 아래와 같이 docstring 작성하여 관련 설명을 추가하는 것을 권장
명시성 향상을 위해 타입 지정 권장
def divide(a: float, b: float) -> float: """a를 b로 나눔 Raises: ValueError: b가 0일 때 나눗셈을 할 수 없다 """ try: return a / b except ZeroDivisionError: return ValueError('잘못된 입력') try: result = divide(x, y) except ValueError: print('잘못된 입력') else: print(f'결과: {result}')
Python
복사
Why
함수에서 None 반환 시 ‘0 또는 False’ 반환에 따른 값 처리와 혼동될 수 있다
→ 조건문에서 None과 0와 False는 모두 False로 평가된다
‘None 반환’은 ‘예외 발생’과 논리적 연관성이 떨어진다
→ None은 ‘값이 지정되지 않음'을 뜻하므로 논리적으로 ‘예외 발생'과 직접적인 연관이 없다

Reference

Brett Slatkin, Effective Python 2nd Edition, https://effectivepython.com/
(번역명) 파이썬 코딩의 기술, https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=254321728