파이썬 함수의 디폴트 매개변수는 호출 시점을 따르지 않는다

종욱·2020년 2월 23일
0

python

목록 보기
1/1

파이썬 함수의 디폴트 매개변수는 호출 시점을 따르지 않는다

mutable한 변수를 함수의 디폴트 매개변수로 사용 시 의도치않은 동작이 일어날 수 있다.

mutable 하다는 것은 변수에 할당된 값을 변경할 수 있다는 의미이다.

다음 예제를 보자.

>>> def func(a=[]): 
...     a.append(5)
...     return id(a)
>>>
>>> func.__defaults__
([],)
>>> func()
4516110408
>>> func()
4516110408
>>> func([3])
4516185480
>>> func.__defaults__
([5, 5],)
>>> id(func.__defaults__[0]) == func()
True

예제를 통해 알 수 있는 건,

  1. 함수의 __defaults__ 키워드로 내부에 저장된 기본 매개변수 값을 볼 수 있다.
  2. 파라미터 a에 값을 지정하지 않고 넘길때와 지정할 때 반환되는 주소값이 다르다.
  3. func 함수는 mutable 한 기본 매개변수를 갖기때문에 a에 값을 주지 않은 채로 호출 하면 값이 누적된다.

실제로 경험한 삽질..

실제로 서비스 중 문제를 겪었던 코드는 대략 다음과 같았다.

from datetime import date

def get_semseter_with_year(date_info=date.today()):
  return date_info.year, get_semseter(date_info)

파라미터를 넣지않으면 datetime 에서 현재시각을 얻어와서 연도/학기 정보를 주는 함수다.
실제 시간 기준으로 2019년 12월에 해당할 때는 문제를 인지하지 못했고,
해가 바뀌어 새 학기가 열리는 시점에 문제가 터졌다.
호출부에서 파라미터를 넘기지않아 현재 날짜 기준으로 함수가 동작해야하는데,
잘못된 값을 리턴해서 버그가 생긴 것이다.

이유는 함수가 정의되는 시점에 함수의 결과가 저장된다.

print(get_semester_with_year.__defaults__)

함수의 __defaults__ 키워드를 출력해보면
함수 정의 시점의 datetime 객체가 들어있음이 확인 가능하다.

def get_semester_with_year(date_info=None):
  if date_info is None:
    date_info = date.today()
  return date_info.year, get_semester(date_info)

의도대로 동작하도록 이렇게 수정하여 해결할 수 있다.

결론은 다음과 같다.
기본매개변수에 함수 호출부를 지정한 경우, 실행 결과가 저장될 뿐이다.

>>> def bar(a=input('함수 호출없이 이 메시지가 보일겁니다. 값을 입력해보세요 : ')):
...     return a
...
함수 호출없이 이 메시지가 보일겁니다. 값을 입력해보세요 : hello world!!
>>> bar(123)
123
>>> bar()
'hello world!!'
>>>

위 처럼 함수만 선언해도 input 메시지를 볼수 있다.
a에 값 전달 없이 bar함수를 호출하면 bar.__defaults__ 에 저장된 방금 입력한 값이 계속 리턴됨을 확인할 수 있다.

Django Model Field의 디폴트값으로 함수를 설정하면 매번 똑같은 값이 나오는 것도 위와 같은 이유다.


References

0개의 댓글