여기서는 함수 자체를 만드는 법보다 호출자가 어떻게 읽고 넘길지를 먼저 봅니다.
순서만으로 충분한지, 이름으로 강제해야 하는지, 개수가 가변적인지, 확장용 옵션을 열어 둘지에 따라 위치 인수, 기본값, *args, **kwargs, 키워드 전용 인수, 위치 전용 인수가 갈립니다.
숏컷 코드
def report(name, score, *, passed=True, extra):
status = "PASS" if passed else "FAIL"
return {"name": name, "score": score, "status": status, extra}
def add_all(*numbers):
return sum(numbers)문법
기본형은 위치 전용, 위치/키워드, 기본값, 가변 위치 인수, 키워드 전용, 가변 키워드 인수를 함께 보면 됩니다.
def func(a, /, b, c=0, *args, option=True, **kwargs):
.../ 앞은 위치 전용 인수이고, * 뒤는 키워드 전용 인수입니다. 이 두 표식까지 읽을 줄 알면 Python 함수 시그니처를 훨씬 정확하게 해석할 수 있습니다.
호출 설계
어떤 호출 방식을 보이게 할 것인가
짧고 명확한 입력 두세 개라면 위치 인수만으로도 충분합니다. 예를 들어 좌표, 폭과 높이, 시작값과 끝값처럼 순서가 자연스럽게 읽히는 경우입니다.
def area(width, height):
return width * height
area(3, 4)하지만 매개변수 의미가 금방 헷갈리기 시작하면 이름을 붙이는 쪽으로 넘어가는 편이 낫습니다.
같은 타입의 인수가 여러 개 있거나, 불리언 옵션처럼 뜻이 중요하면 키워드 인수가 더 읽기 쉽습니다.
def connect(host, port, timeout=3):
return host, port, timeout
connect("localhost", 8000, timeout=5)호출부에서 True, False, 숫자 옵션이 연달아 보이면 특히 이름을 붙이는 편이 안전합니다.
# 덜 읽히는 호출
connect("localhost", 8000, 5)
# 의미가 드러나는 호출
connect("localhost", 8000, timeout=5)외부 호출 이름을 고정하고 싶거나, 의미가 없는 내부 이름을 숨기고 싶으면 위치 전용 인수를 씁니다.
def clamp(value, minimum, maximum, /):
return max(minimum, min(value, maximum))호출자는 clamp(10, 0, 100)처럼 쓰지만 value=10 같은 이름 기반 호출은 허용되지 않습니다.
옵션은 이름으로만 받게 하고 싶으면 * 뒤에 둡니다.
def save(path, *, overwrite=False, encoding="utf-8"):
...
save("note.txt", overwrite=True)옵션이 많아질수록 이 패턴은 실수 방지에 큰 도움이 됩니다.
가변 인자
*args와 **kwargs를 언제 여는가
*args는 남는 위치 인수를 튜플로 받습니다.
즉 "값을 몇 개 받을지 미리 고정하지 않는다"는 뜻입니다.
def add_all(*numbers):
return sum(numbers)
def show_options(**kwargs):
return kwargssum, print처럼 여러 값을 모으는 패턴에는 잘 맞지만, 실제로는 고정 인수가 더 읽기 쉬운 함수까지 무조건 *args로 열어 두는 건 피하는 편이 좋습니다.
def add_all(*numbers):
return sum(numbers)
add_all(1, 2, 3)**kwargs는 남는 키워드 인수를 dict로 받습니다.
래퍼 함수, 옵션 전달기, 확장 필드 수집처럼 "이름 붙은 추가 옵션"을 열어 둘 때 유용합니다.
def render_text(text, **style):
return {"text": text, "style": style}다만 핵심 인자까지 전부 **kwargs 뒤로 숨기면 API가 흐려집니다.
자주 쓰는 인자와 중요한 인자는 먼저 시그니처에 드러내고, 정말 추가 옵션만 **kwargs로 받는 편이 낫습니다.
def render_text(text, **style):
return {"text": text, "style": style}
render_text("hello", color="red", weight="bold")주의할 점
*args, **kwargs가 편하다고 모든 인자를 숨겨 버리면 API가 흐려집니다. 자주 쓰는 인자는 명시적으로 선언하는 편이 좋습니다.
# ❌ 함수가 실제로 무엇을 받는지 불명확
def create_user(*args, kwargs):
...
# ✅ 중요한 인자는 시그니처에 드러낸다
def create_user(name, email, *, is_admin=False, extra):
...# ❌ 키워드 전용으로 만들고 싶은 옵션인데 위치 인수로 받게 둠
def save(path, overwrite=False):
...
# ✅ 의미가 중요한 옵션은 * 뒤로 보내 이름을 강제
def save(path, *, overwrite=False):
...참고 링크
2 sources