LBYL — Look Before You Leap. 연산이 성공할지 체크, 그렇다면 실행. if key in d: return d[key]. EAFP — Easier to Ask Forgiveness than Permission. 그냥 시도, 실패하면 예외 처리. try: return d[key]; except KeyError: return default.
왜 Python 은 EAFP 선호
세 이유. (1) Python 예외는 안 발생할 때 싸 (성공 path 에 추가 사이클 약간), 발생할 때만 비싸. 보통 성공하는 연산엔 EAFP path 가 빨라. (2) LBYL 은 race condition — 파일 존재 체크 후 열기, 사이에 사라질 창. (3) EAFP 가 "happy path" 로직 도드라지게 — LBYL 은 체크로 어지럽혀.
LBYL 이 여전히 맞을 때
실패 케이스가 흔할 때 (대부분 lookup 이 miss), 예외가 느려져. 연산에 못 되돌리는 부작용 있을 때 (파일 절반 썼는데 실패), 중간 실패가 시작 안 한 것보다 나빠. 부작용 없이 state 진짜로 query 해야 하면 LBYL 이 맞음.
dict idiom — .get vs in vs try
dict 접근에 세 idiomatic 방법. d[key] 가 miss 시 KeyError (EAFP). d.get(key, default) 가 조용히 디폴트 반환. if key in d: ... 가 LBYL. 누락 케이스가 예외적인지, 기대된지, 둘 다인지에 따라. 보편적으로 맞는 거 없음.
Pythonic Way: "시도, 실패 시 fallback" 흔한 패턴엔 EAFP 디폴트. 체크가 진짜로 더 쌀 때, 연산에 부작용 있을 때, "이게 가능하긴 한가" 체크 자체가 질문일 때 LBYL. 둘 공존 — 의도 더 명확히 표현하는 거 사용.
Code
LBYL vs EAFP — 같은 문제, 다른 스타일·python
d = {"x": 1, "y": 2}
# LBYL — Look Before You Leap
if "x" in d:
value = d["x"]
else:
value = 0
# EAFP — Easier to Ask Forgiveness than Permission
try:
value = d["x"]
except KeyError:
value = 0
# Python 에선 EAFP 가 일반적으로 idiomatic.
# 근데 dict 에 한정해선 .get() 이 더 좋음:
value = d.get("x", 0)
성공 path 가 흔할 때 EAFP 가 속도로 이김·python
import time
d = {i: i*2 for i in range(10000)}
# LBYL — 매 접근마다 체크
def lbyl_sum():
total = 0
for i in range(10000):
if i in d:
total += d[i]
return total
# EAFP — 그냥 접근, 드문 miss 만 raise
def eafp_sum():
total = 0
for i in range(10000):
try:
total += d[i]
except KeyError:
pass
return total
# 대부분 접근 성공할 때 EAFP 가 빠름 (이중 체크 X)
start = time.perf_counter(); lbyl_sum(); print("LBYL:", time.perf_counter() - start)
start = time.perf_counter(); eafp_sum(); print("EAFP:", time.perf_counter() - start)
EAFP 가 race condition 회피·python
import os
# LBYL — RACE CONDITION
if os.path.exists("data.txt"):
# 체크와 open 사이에 파일 삭제되면?
with open("data.txt") as f:
contents = f.read()
# 동시성 / 외부 파일시스템 이벤트에 버그.
# EAFP — atomic
try:
with open("data.txt") as f:
contents = f.read()
except FileNotFoundError:
contents = ""
# 체크와 사용 사이 창 없음. open 성공 또는 X.
LBYL 이 맞을 때 — 부작용 케이스·python
# 부작용 있는 연산은 EAFP 가 절반에서 멈추면 나쁨
def create_directory(path):
# LBYL — 먼저 체크, 안전할 때만 행동
if os.path.exists(path):
raise FileExistsError(f"{path} 이미 존재")
os.makedirs(path)
# os.makedirs 그냥 시도하고 FileExistsError 잡았다면
# 더 복잡한 체크 (우리 거? 기존?)
# 여기선 LBYL 이 더 깔끔 — 연산이 non-idempotent.
count_word(haystack, needle) 두 버전 — 문자열 list haystack 에서 needle 발생 횟수. (a) LBYL — 카운트 전에 isinstance(s, str) + needle in s 체크. (b) EAFP — 그냥 s.count(needle) 호출, non-string 의 AttributeError 잡음. 일부 비-문자열 항목 포함 list 로 둘 다 테스트 — 둘 다 맞는 카운트 확인. 어느 게 Python 디폴트에 더 가까운지 논의.
Progress
Progress is local-only — sign in to sync across devices.