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

- Author: @mildsalmon
- Published: 2022-04-03
- Updated: 2022-04-03
- Source: http://blex.me/@mildsalmon/16-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%95%A8%EC%88%98%EC%9D%98-%EB%94%94%ED%8F%B4%ED%8A%B8-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EB%8A%94-%ED%98%B8%EC%B6%9C-%EC%8B%9C%EC%A0%90%EC%9D%84-%EB%94%B0%EB%A5%B4%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4
- Tags: python

---

mutable한 변수를 함수의 디폴트 매개변수로 사용 시 의도치않은 동작이 일어날 수 있다.

```python

>>> def func(a=1, b=[]):
>>>     b.append(10)
>>>     print(b)
>>>     return id(a), id(b)

>>> func.__defaults__
(1, [])

>>> func()
[10]
(140713432621456, 1728853850056)

>>> func()
[10, 10]
(140713432621456, 1728853850056)

>>> func(a=20, b=[30])
[30, 10]
(140713432622064, 1728853898696)

>>> func.__defaults__
(1, [10, 10])

```

위 예제를 통해 아래를 유추할 수 있다.

1. 함수의 `__defaults__` 키워드로 내부에 저장된 기본 매개변수 값을 볼 수 있다.
2. 파라미터 a에 값을 지정하지 않고 넘길때와 지정할 때 반환되는 주소값이 다르다.
3. `func`함수는 mutable한 기본 매개변수를 갖기 때문에 b에 값을 주지 않은 채로 호출하면 값이 누적된다.

## A. 예제

### a. 디폴트 매개변수에 mutable 객체를 선언하여 문제 발생

```python

>>> import datetime
>>> import time

>>> # stopwatch 함수 선언
>>> def stopwatch(date_info = []):
>>>     date_info.append(datetime.datetime.now())
>>>     return date_info

>>> print("##### Phase 1 #########")
>>> print(f'stopwatch 함수 선언 후 바로 실행\n\t{stopwatch()}')
>>> print(f'stopwatch 함수의 defaults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> print("##### Phase 2 #########")
>>> print(f'stopwatch 실행\n\t{stopwatch()}')
>>> print(f'stopwatch 함수의 defaults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> print("##### Phase 3 #########")
>>> print(f'stopwatch 새로 실행\n\t{stopwatch([])}')
>>> print(f'stopwatch 함수의 defaults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> print("##### Phase 4 #########")
>>> print(f'stopwatch 실행\n\t{stopwatch()}')
>>> print(f'stopwatch 함수의 defaults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()

------

##### Phase 1 #########
stopwatch 함수 선언 후 바로 실행
	[datetime.datetime(2022, 4, 3, 9, 48, 9, 400298)]
stopwatch 함수의 defaults 매개변수
	([datetime.datetime(2022, 4, 3, 9, 48, 9, 400298)],)
현재 시간
	2022-04-03 09:48:09.400298
	
##### Phase 2 #########
stopwatch 실행
	[datetime.datetime(2022, 4, 3, 9, 48, 9, 400298), datetime.datetime(2022, 4, 3, 9, 48, 19, 411539)]
stopwatch 함수의 defaults 매개변수
	([datetime.datetime(2022, 4, 3, 9, 48, 9, 400298), datetime.datetime(2022, 4, 3, 9, 48, 19, 411539)],)
현재 시간
	2022-04-03 09:48:19.411539
	
##### Phase 3 #########
stopwatch 새로 실행
	[datetime.datetime(2022, 4, 3, 9, 48, 29, 411675)]
stopwatch 함수의 defaults 매개변수
	([datetime.datetime(2022, 4, 3, 9, 48, 9, 400298), datetime.datetime(2022, 4, 3, 9, 48, 19, 411539)],)
현재 시간
	2022-04-03 09:48:29.411675
	
##### Phase 4 #########
stopwatch 실행
	[datetime.datetime(2022, 4, 3, 9, 48, 9, 400298), datetime.datetime(2022, 4, 3, 9, 48, 19, 411539), datetime.datetime(2022, 4, 3, 9, 48, 39, 423540)]
stopwatch 함수의 defaults 매개변수
	([datetime.datetime(2022, 4, 3, 9, 48, 9, 400298), datetime.datetime(2022, 4, 3, 9, 48, 19, 411539), datetime.datetime(2022, 4, 3, 9, 48, 39, 423540)],)
현재 시간
	2022-04-03 09:48:39.423540
	
```

1. default 매개변수가 계속 누적되는 것을 볼 수 있다.
2. stopwatch를 재실행해도 Phase 4에서는 이전에 실행했던 stopwatch 기록으로 기록이 진행된다.

### b. 해결 방법

```python

>>> import datetime
>>> import time

>>> # stopwatch 함수 선언
>>> def stopwatch(date_info = None):
>>>     if date_info is None:
>>>         date_info = []
>>>     date_info.append(datetime.datetime.now())
>>>     return date_info

>>> record = None

>>> print("##### Phase 1 #########")
>>> print(f'stopwatch 함수 선언 후 바로 실행\n\t{stopwatch(record)}')
>>> print(f'stopwatch 함수의 defqults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> print("##### Phase 2 #########")
>>> print(f'stopwatch 실행\n\t{stopwatch(record)}')
>>> print(f'stopwatch 함수의 defqults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> record = []

>>> print("##### Phase 3 #########")
>>> print(f'stopwatch 새로 실행\n\t{stopwatch(record)}')
>>> print(f'stopwatch 함수의 defqults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()
>>> time.sleep(10)

>>> print("##### Phase 4 #########")
>>> print(f'stopwatch 실행\n\t{stopwatch(record)}')
>>> print(f'stopwatch 함수의 defqults 매개변수\n\t{stopwatch.__defaults__}')
>>> print(f'현재 시간\n\t{datetime.datetime.now()}')
>>> print()

-------

##### Phase 1 #########
stopwatch 함수 선언 후 바로 실행
	[datetime.datetime(2022, 4, 3, 10, 6, 25, 272702)]
stopwatch 함수의 defqults 매개변수
	(None,)
현재 시간
	2022-04-03 10:06:25.272702
	
##### Phase 2 #########
stopwatch 실행
	[datetime.datetime(2022, 4, 3, 10, 6, 35, 274798)]
stopwatch 함수의 defqults 매개변수
	(None,)
현재 시간
	2022-04-03 10:06:35.274798
	
##### Phase 3 #########
stopwatch 새로 실행
	[datetime.datetime(2022, 4, 3, 10, 6, 45, 280475)]
stopwatch 함수의 defqults 매개변수
	(None,)
현재 시간
	2022-04-03 10:06:45.280475
	
##### Phase 4 #########
stopwatch 실행
	[datetime.datetime(2022, 4, 3, 10, 6, 45, 280475), datetime.datetime(2022, 4, 3, 10, 6, 55, 280664)]
stopwatch 함수의 defqults 매개변수
	(None,)
현재 시간
	2022-04-03 10:06:55.280664

```

## B. 결론

python의 default 매개변수는 함수를 생성하는 그 시점에 초기화되며, list와 같은 mutable 객체는 그 값이 변경되면서 예상하지 못한 결과를 출력할 수 있다.

# 참고문헌

[1] [ggg](https://velog.io/@ggg). https://velog.io/@ggg/Python-default-parameter. Velog. (accessed Apr 3. 2022)

[2] [ash84](https://blog.weirdx.io/post/author/ash84 "Posts by ash84"). https://blog.weirdx.io/post/44132. Blog. (accessed Apr 3. 2022)

[3] https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument?page=1&tab=scoredesc#tab-top. Stack Overflow(accessed Apr 3. 2022)
