대부분 "unpythonic" 코드가 잘못 X — 작동. 단지 불필요한 indirection, 무게 못 이기는 방어 코딩, 다른 언어에서 import 한 스타일. 이 패턴 인식 + 가지치기가 코드 짧게, 빨리 읽기, 유지 쉽게.
1. range(len(seq)) — 진짜 enumerate 원할 때
seq[i] 접근 위해 for i in range(len(seq)) 반복하면 거의 확실히 for i, x in enumerate(seq) 원함. 예외 — 진짜 값 안 필요할 때 (인덱스만, 어떤 계산용). 그래도 자문해.
2. None 비교에 == 사용
x == None 작동하지만 idiom X. x is None / x is not None 사용. None 이 싱글톤, identity 비교가 빠르고 더 잘 읽힘. True, False 비교하는 드문 경우에도 같음.
3. 장황한 dict 반복
키 view 의 key.foo() 안 쓰는데 for key in d.keys(): — 그냥 for key in d:. for key in d.keys(): print(d[key]) — 그냥 for value in d.values():. for key in d.keys(): print(key, d[key]) — 그냥 for key, value in d.items():.
4. 디폴트 mutable 인자
def f(x=[]) — 반복 다룸. 공유 디폴트 list 버그. def f(x=None): if x is None: x = [] 사용. 외워, 버그 미묘 + 도입 쉬움.
5. 너무 넓게 잡기
except: 가 KeyboardInterrupt 포함 BaseException 잡음. except Exception: 이 넓지만 받아들일 만. 더 좋음 — 회복 가능한 특정 예외 타입 잡음. 다른 예외 전파해야.
6. 측정 없이 최적화
premature 최적화가 버그 도입 종류. profile 먼저 (tooling 트랙), 시간 실제 있는 곳 최적화. 측정 전에 array skip, C extension 짜기, vectorize X — 안 느렸던 코드에 시간 보냄.
Code
range(len()) — 보통 잘못·python
items = ["a", "b", "c"]
# UNPYTHONIC
for i in range(len(items)):
print(i, items[i])
# PYTHONIC
for i, x in enumerate(items):
print(i, x)
# 페어 반복엔 zip
names = ["alice", "bob"]
ages = [30, 25]
# UNPYTHONIC
for i in range(len(names)):
print(names[i], ages[i])
# PYTHONIC
for name, age in zip(names, ages):
print(name, age)
is None vs == None·python
x = None
# 덜 idiomatic
if x == None:
print("missing")
# Idiomatic
if x is None:
print("missing")
if x is not None:
print("present")
# 드문 경우의 True/False 도 같음
# (그 경우엔 보통 그냥 `if x:` 또는 `if not x:` 가 더 좋음)
Dict 반복 — 맞는 view 골라·python
d = {"a": 1, "b": 2, "c": 3}
# UNPYTHONIC
for key in d.keys():
print(key, d[key])
# PYTHONIC
for key, value in d.items():
print(key, value)
# 값만
for value in d.values():
print(value)
# 키만
for key in d: # dict 반복하면 키
print(key)
Mutable 디폴트 — 명백한 버그 fix·python
# 안 좋음 — 공유 디폴트 list
def append_to(item, target=[]):
target.append(item)
return target
append_to("a") # ['a']
append_to("b") # ['a', 'b'] <- 버그
# 좋음 — None sentinel 패턴
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return target
append_to("a") # ['a']
append_to("b") # ['b'] <- 새 list
너무 넓게 잡기·python
# 안 좋음 — bare except
try:
risky()
except:
pass
# KeyboardInterrupt, SystemExit, MemoryError 등 잡음.
# 받아들일 만 — Exception 이 가장 넓은 합리적
try:
risky()
except Exception as e:
logger.error("unexpected: %s", e)
# 가장 좋음 — 특정
try:
risky()
except (ValueError, KeyError) as e:
handle_specifically(e)
# 다른 예외 전파 — 보통 맞음
자체 작업 또는 어떤 오픈소스 Python 프로젝트에서 이 안티패턴 중 하나 보이는 코드 셋 찾아 — range(len()), bare except, mutable default, ==None. 각각 교정 버전. 자체 코드에 못 찾으면 일부러 나쁜 예 셋 + 다시 짜. 핵심 — 인식, 못 보는 패턴 fix X.
Progress
Progress is local-only — sign in to sync across devices.