Python 강좌 / 위치·키워드 인수와 기본값 사용하기

함수 인수는 위치나 이름으로 전달할 수 있고 기본값으로 선택 항목을 만들 수 있습니다. 리스트 같은 변경 가능한 객체를 기본값으로 두면 호출 사이에 상태가 공유될 수 있으므로 주의해야 합니다.

인수 전달 순서와 읽기 쉬운 호출

위치 인수는 순서대로 매개변수에 연결되고 키워드 인수는 이름으로 연결됩니다. 같은 매개변수에 두 번 값을 주거나 키워드 인수 뒤에 일반 위치 인수를 넣으면 오류입니다. 선택지가 여러 개일 때 키워드로 의미를 적으면 호출부를 읽기 쉽습니다.

관련 동작과 지원 조건은 Python: controlflow에서 확인할 수 있습니다.

매개변수 목록의 * 뒤에 오는 항목은 키워드 전용입니다. 아래 예제에서 currency는 이름을 써서 전달해야 하므로 단위가 무엇인지 호출부에 드러납니다.

기본값의 객체 공유와 호출 인수은 변수의 범위와 변경 가능한 객체의 동작 이해하기 및 가변 인수와 언패킹: *args·**kwargs에서 사용하는 규칙과 연결됩니다.

전체 예제와 실행 결과

별도의 실습 폴더에서 아래 파일을 UTF-8로 저장합니다. 가상환경을 선택한 터미널에서 python arguments.py 명령으로 실행합니다. macOS·Ubuntu에서 가상환경을 사용하지 않는 경우 명령 이름을 python3으로 바꿉니다.

arguments.py 파일의 전체 내용입니다.

def format_expense(name, amount, *, currency="KRW"):
    return f"{name}: {amount:,} {currency}"

def append_name(name, names=None):
    if names is None:
        names = []
    names.append(name)
    return names

print(format_expense("lunch", 8000))
print(format_expense(amount=9000, name="dinner", currency="KRW"))
print(append_name("A"))
print(append_name("B"))

예상 결과는 다음과 같습니다.

lunch: 8,000 KRW
dinner: 9,000 KRW
['A']
['B']

기본값은 언제 만들어지나요?

기본값 표현식은 호출할 때마다 새로 평가되는 것이 아니라 함수 정의를 실행할 때 평가됩니다. def append_name(name, names=[])처럼 쓰면 같은 리스트가 재사용됩니다. None을 기본값으로 받고 함수 안에서 새 리스트를 만드는 형태가 일반적인 해결 방법입니다.

names가 빈 리스트일 때도 호출자가 전달한 목록을 쓰려면 if names is None으로 검사합니다. if not names라고 쓰면 빈 리스트를 새 리스트로 바꿔 호출자의 의도와 달라질 수 있습니다.

기본 인수는 호출 때마다 새로 만드는 것이 아니라 함수 정의가 실행될 때 평가됩니다. 다음 두 함수를 같은 파일에서 연속 호출하면 첫 함수는 A를 유지한 채 B를 추가하고, None을 기본값으로 쓰는 함수는 호출마다 새 목록을 만듭니다.

아래 전체 코드를 defaults.py로 저장하고 같은 실습 폴더에서 python defaults.py로 실행합니다.

def shared(name, names=[]):
    names.append(name)
    return names
def fresh(name, names=None):
    if names is None:
        names = []
    names.append(name)
    return names
print(shared("A"))
print(shared("B"))
print(fresh("A"))
print(fresh("B"))

예상 결과:

['A']
['A', 'B']
['A']
['B']

선택 항목과 잘못된 호출 검증하기

currency를 세 번째 위치 인수로 전달해 키워드 전용 규칙이 오류로 드러나는지 확인합니다. 또한 외부에서 names=[]를 만들고 append_name("A", names)를 호출하면 그 리스트 자체가 바뀌는지 확인하세요. 객체 변경 여부는 기본값 문제와 별도로 함수의 계약으로 밝혀야 합니다.

날짜나 현재 시각도 기본값에 직접 계산하면 정의 시점 값이 남을 수 있습니다. 호출마다 새 값이 필요하면 None을 받고 함수 본문에서 계산하는 방식을 사용합니다.