Chapter 3

함수 설계

  • 3.1 함수 인자 변경에 주의
  • 3.2 3개 초과 변수 언패킹 금지
  • 3.3 None 반환 대신 예외 발생
  • 3.4 클로저와 nonlocal
  • 3.5 가변 위치 인자(*args)
  • 3.6 키워드 인자로 선택적 동작
  • 3.7 동적 디폴트 인자는 None으로
  • 3.8 위치 전용과 키워드 전용 인자
  • 3.9 functools.wraps로 데코레이터 작성
  • 3.10 functools.partial로 글루 함수

함수 인터페이스를 잘 설계하면 호출하는 쪽에서 실수할 여지가 사라져. 이 장의 핵심은 그거야 — 함수가 사용자를 보호해야 한다는 것.

가장 흔한 실수가 가변 디폴트 인자야. def append_to(element, target=[]) 이렇게 쓰면 모든 호출이 같은 리스트를 공유하는 끔찍한 버그가 생기거든. 파이썬에서 디폴트 인자는 함수 정의 시점에 한 번만 평가되니까, 가변 객체를 디폴트로 쓰면 안 돼. 정석은 target=None으로 두고 함수 안에서 생성하는 거지. 같은 맥락에서, 가변 객체를 함수에 넘기면 원본이 변경될 수 있다는 것도 항상 의식해야 해.

함수가 실패했을 때 None을 반환하는 것도 위험해. 0이나 빈 문자열 같은 유효한 값과 구분이 안 되거든 — if not result에서 None0이 똑같이 False로 평가되니까. 실패 시에는 예외를 발생시켜야 호출자가 명시적으로 처리할 수 있어. 반환값이 3개를 넘으면 순서가 헷갈려지니까 dataclass나 namedtuple로 결과 객체를 만드는 게 안전하고.

Python 3.8의 위치 전용(/)과 키워드 전용(*) 인자도 API 설계에 강력한 도구야. 위치 전용은 내부 인자 이름을 자유롭게 바꿀 수 있게 해주고, 키워드 전용은 True/False 같은 불리언 플래그의 의미를 명확히 하지. 데코레이터를 만들 때는 **@functools.wraps**를 반드시 써야 원래 함수의 이름과 독스트링이 보존되고, 인자를 고정해서 새 함수를 만들 때는 lambda보다 **functools.partial**이 의도가 명확해.


정리

3장 읽고 기억할 거 세 가지:

  1. 가변 디폴트 인자는 절대 쓰지 마라. None을 디폴트로 두고 함수 안에서 생성하는 게 정석이다.
  2. 실패 시 None 대신 예외를 발생시키고, 반환값이 3개를 넘으면 전용 객체를 쓰자. None은 유효한 값과 구분이 안 된다.
  3. 위치 전용/키워드 전용 인자로 API를 설계하고, 데코레이터에는 @wraps, 글루 함수에는 partial을 쓰자.