default value를 설정하면 객체를 생성하거나 함수 호출 시 매개변수가 누락되었을 때 해당 값으로 초기화 된다.
그러나 default value로 list나 dict같은 mutable 값을 사용하면, 호출할 때마다 새로 생성하는 것이 아니라 이전에 생성했던 객체를 사용하고 있다는 사실을 알았다.
python 공식문서에서는 이에 대해 "보통 함수가 호출될 때마다 default value로 새로운 객체를 생성할 것이라 예상하지만, 실제로는 그렇지 않다"고 말한다. default value는 함수가 정의될 때 딱 한번만 생성되므로, dict를 default value로 생성하고 수정했다면 이후의 호출에서도 수정된 dict를 그대로 사용한다는 것이다.
Why are default values shared between objects?
The first time you call this function, mydict contains a single item. The second time, mydict contains two items because when foo() begins executing, mydict starts out with an item already in it.
It is often expected that a function call creates new objects for default values. This is not what happens. Default values are created exactly once, when the function is defined. If that object is changed, like the dictionary in this example, subsequent calls to the function will refer to this changed object.
By definition, immutable objects such as numbers, strings, tuples, and None, are safe from change. Changes to mutable objects such as dictionaries, lists, and class instances can lead to confusion.
Because of this feature, it is good programming practice to not use mutable objects as default values. Instead, use None as the default value and inside the function, check if the parameter is None and create a new list/dictionary/whatever if it is.
공식문서는 default value로 None을 주고 함수 내에서 이를 체크하고 mutable 객체를 생성하는 것을 추천한다.
def foo(mydict=None):
if mydict is None:
mydict = {} # create a new dict for local namespace
default value의 이러한 특징을 활용한 caching 방법이다. 기존에 계산한 결과를 _cache
에 저장하고 제공한다.
# Callers can only provide two parameters and optionally pass _cache by keyword
def expensive(arg1, arg2, *, _cache={}):
if (arg1, arg2) in _cache:
return _cache[(arg1, arg2)] # 함수가 호출될 때 같은 _cache를 공유한다는 것을 이용한다.
# Calculate the value
result = ... expensive computation ...
_cache[(arg1, arg2)] = result # Store result in the cache
return result