C.W.K.
Stream
Lesson 02 of 06 · published

EAFP vs LBYL — Python 이 EAFP 선호하는 이유

~15 min · eafp, lbyl, idiom, ask-forgiveness

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

두 에러 처리 철학

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.

External links

Exercise

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.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.