Python 함수에서는 매개변수에 미리 기본값을 지정할 수 있습니다. 이를 디폴트 파라미터(Default Parameter), 또는 기본 매개변수라고 합니다.
기본값을 사용하면 함수를 호출할 때 특정 인자를 생략할 수 있어 편리합니다. 하지만 특히 리스트나 딕셔너리처럼 값을 변경할 수 있는 객체를 기본값으로 사용하면 예상하지 못한 결과가 발생할 수 있습니다.
이번 글에서는 Python 디폴트 파라미터의 기본 사용법과 함께, 실무에서 자주 실수하는 부분을 살펴보겠습니다.
디폴트 파라미터란?
함수의 매개변수에 기본값을 지정하는 것을 디폴트 파라미터라고 합니다.
def greet(name, message="안녕하세요"):
print(message, name)
message에 "안녕하세요"라는 기본값을 지정했습니다.
따라서 message를 전달하지 않고 호출해도 정상적으로 실행됩니다.
greet("철수")
결과:
안녕하세요 철수
원한다면 호출할 때 다른 값을 전달할 수도 있습니다.
greet("철수", "반갑습니다")
결과:
반갑습니다 철수
즉, 값을 전달하지 않으면 기본값을 사용하고 값을 전달하면 전달한 값이 사용됩니다.
디폴트 파라미터의 기본 규칙
기본값이 있는 매개변수와 기본값이 없는 매개변수를 함께 사용할 수도 있습니다.
def introduce(name, age=20):
print(name, age)
이 경우 name은 반드시 값을 전달해야 하지만 age는 생략할 수 있습니다.
introduce("철수")
결과:
철수 20
다음처럼 직접 값을 전달할 수도 있습니다.
introduce("철수", 25)
결과:
철수 25
여기서 중요한 규칙이 하나 있습니다.
기본값이 없는 매개변수는 기본값이 있는 매개변수보다 앞에 작성해야 합니다.
올바른 예:
def example(a, b=10):
pass
잘못된 예:
def example(a=10, b):
pass
두 번째 코드는 문법 오류가 발생합니다.
리스트를 기본값으로 사용하면 생기는 문제
디폴트 파라미터에서 특히 주의해야 하는 부분이 변경 가능한 객체를 기본값으로 사용하는 경우입니다.
다음 코드를 보겠습니다.
def add_item(item, items=[]):
items.append(item)
return items
겉으로 보면 별다른 문제가 없어 보입니다.
처음 호출하면 다음과 같습니다.
print(add_item("Python"))
결과:
['Python']
그런데 다시 호출해 보겠습니다.
print(add_item("Java"))
예상과 달리 결과는 다음과 같습니다.
['Python', 'Java']
함수를 새로 호출했는데 이전에 추가했던 "Python"까지 남아 있습니다.
왜 이런 일이 발생할까요?
Python의 함수 기본값은 함수를 호출할 때마다 새로 만들어지는 것이 아니라 함수가 정의될 때 한 번 만들어집니다.
따라서 items=[]는 여러 번의 함수 호출에서 같은 리스트 객체를 기본값으로 사용할 수 있습니다.
None을 사용하면 안전하게 처리할 수 있다
이러한 문제를 피하는 대표적인 방법은 기본값으로 None을 사용하는 것입니다.
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
이제 호출할 때마다 새로운 리스트가 만들어집니다.
print(add_item("Python"))
print(add_item("Java"))
결과:
['Python']
['Java']
필요한 경우에는 기존 리스트를 직접 전달할 수도 있습니다.
items = []
add_item("Python", items)
add_item("Java", items)
print(items)
결과:
['Python', 'Java']
즉, None을 기본값으로 설정하고 함수 내부에서 필요한 객체를 생성하는 방법을 사용하면 의도하지 않은 객체 공유를 피할 수 있습니다.
딕셔너리도 같은 문제가 발생한다
리스트뿐만 아니라 딕셔너리처럼 변경 가능한 객체도 주의해야 합니다.
def add_user(name, users={}):
users[name] = True
return users
이 함수를 여러 번 호출하면 이전 호출에서 추가한 값이 계속 남을 수 있습니다.
따라서 다음처럼 작성하는 것이 안전합니다.
def add_user(name, users=None):
if users is None:
users = {}
users[name] = True
return users
이 방식은 리스트뿐만 아니라 set과 같은 다른 변경 가능한 객체를 기본값으로 사용할 때도 동일하게 적용할 수 있습니다.
왜 이런 차이가 생길까?
Python에서 함수의 기본 인자값은 함수 호출 시점마다 새롭게 평가되는 것이 아닙니다.
다음 코드를 통해 확인할 수도 있습니다.
def example(items=[]):
items.append(1)
print(items)
example()
example()
example()
결과:
[1]
[1, 1]
[1, 1, 1]
하나의 기본 리스트가 호출 사이에서 계속 유지되고 있기 때문입니다.
반면 None을 사용하면 함수가 호출될 때마다 새로운 리스트를 생성할 수 있습니다.
def example(items=None):
if items is None:
items = []
items.append(1)
print(items)
example()
example()
example()
결과:
[1]
[1]
[1]
이 차이를 이해하면 디폴트 파라미터에서 발생하는 문제를 훨씬 쉽게 이해할 수 있습니다.
기본값에는 변경되지 않는 객체를 사용할 수 있다
모든 기본값이 문제가 되는 것은 아닙니다.
정수, 문자열, 튜플처럼 변경할 수 없는 객체를 기본값으로 사용하는 것은 일반적으로 문제가 없습니다.
def calculate(price, discount=0.1):
return price * (1 - discount)
여기서 discount의 기본값은 0.1입니다.
print(calculate(10000))
결과:
9000.0
필요하면 다른 값을 전달할 수도 있습니다.
print(calculate(10000, 0.2))
결과:
8000.0
이처럼 단순한 설정값을 기본값으로 지정하는 것은 디폴트 파라미터의 대표적인 활용 방법입니다.
실무에서는 어떻게 사용하는 것이 좋을까?
디폴트 파라미터는 함수의 사용성을 높여주는 유용한 기능입니다.
예를 들어 데이터 처리 함수에서 옵션을 기본값으로 지정할 수 있습니다.
def load_data(path, encoding="utf-8"):
# 데이터 로딩
pass
대부분의 경우 기본 인코딩을 사용하고 특별한 경우에만 다른 값을 전달하도록 만들 수 있습니다.
load_data("data.csv")
필요하면 직접 지정할 수도 있습니다.
load_data("data.csv", encoding="cp949")
이처럼 기본값은 함수의 기본적인 동작을 정해 두고 필요한 경우에만 사용자가 변경하도록 만들 때 유용합니다.
디폴트 파라미터 핵심 정리
이번 글에서 기억해야 할 내용은 많지 않습니다.
- 매개변수에 기본값을 지정할 수 있다.
- 기본값이 있는 인자는 호출할 때 생략할 수 있다.
- 기본값이 없는 매개변수는 기본값이 있는 매개변수보다 앞에 작성한다.
- 리스트나 딕셔너리처럼 변경 가능한 객체를 기본값으로 사용하는 것은 주의한다.
- 변경 가능한 객체가 필요하다면
None을 기본값으로 지정하고 함수 내부에서 생성하는 방법을 사용할 수 있다.
특히 다음과 같은 형태는 기억해 두면 좋습니다.
def example(items=None):
if items is None:
items = []
# 함수 코드
Python에서 디폴트 파라미터를 사용할 때 자주 사용하는 안전한 패턴입니다.
마무리
디폴트 파라미터는 Python 함수를 편리하게 만들어 주는 기능입니다. 단순한 숫자나 문자열 등을 기본값으로 지정하면 함수 호출을 간단하게 만들 수 있습니다.
하지만 리스트나 딕셔너리처럼 변경 가능한 객체를 기본값으로 사용하면 함수 호출 사이에서 객체가 공유될 수 있다는 점을 주의해야 합니다.
따라서 변경 가능한 객체가 필요한 경우에는 None을 기본값으로 사용하고 함수 내부에서 새로운 객체를 생성하는 방법을 알아두는 것이 좋습니다.
다음 글에서는 함수에 전달되는 여러 개의 값을 묶거나 풀어내는 파라미터 언패킹에 대해 살펴보겠습니다.