클로저 기능의 필요성을 확인하기 위해
아래와 같이 defaultdict을 활용하여 result값을 초기화 시키는 코드가 있다.
defaultdict(키가 없을 경우 초기 값, 선언된 dict) 형식에 맞춰 result를 선언하였고,
키가 없을 경우 초기 값에 로그를 보기 위해 숫자가 아닌 log_init 함수로 설정하였다.
( 함수또한 일급객체이므로 참조 값으로 활용 가능 )
def log_init():
print('키가 초기화 됨')
return 0
from collections import defaultdict
current = {'green': 15, 'blue': 7}
increments = [
('red', 6),
('blue', 17),
('orange', 9)
]
result = defaultdict(log_init, current)
print('before:', dict(result))
for key, amount in increments:
result[key] += amount
print('after:', dict(result))
>>>
before: {'green': 15, 'blue': 7}
키가 초기화 됨
키가 초기화 됨
after: {'green': 15, 'blue': 24, 'red': 6, 'orange': 9}
해당 코드에서 초기화 되는 횟수를 알고 싶다고 가정할 때, 클로저를 활용 할 수 있다.
클로저는 내부 함수(예시에선 check_init)를 선언하고, 내부 함수에서 값을 반환한다.
이때 외부함수는 자신이 가진 변수 값 등을 내부함수에 전달한다.
클로저는 내부함수가 외부 함수의 변수에 접근하는 경우,
내부 함수가 외부 함수의 환경을 기억하고 있으며
외부 함수가 종료되어도 내부 함수를 호출할 때마다
외부 함수의변수에 접근할 수 있다.
클로저에 대한 자세한 예시는 아래를 참고하면 이해하기 쉽다.
https://dojang.io/mod/page/view.php?id=2366
파이썬 코딩 도장: 33.3 클로저 사용하기
이제 함수를 클로저 형태로 만드는 방법을 알아보겠습니다. 다음은 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 a * x + b를 계산하는 함수 mul_add를 만든 뒤에 함수 mul_add 자체를 반환합니다. clos
dojang.io
아래 코드를 보면 check_init이라는 내부함수를 통해
초기화 된 횟수를 count 변수 값에 저장할 수 있다.
from collections import defaultdict
def counting_init(current, increments):
added_count = 0
def check_init():
nonlocal added_count
added_count += 1
return 0
result = defaultdict(check_init, current)
for key, amount in increments:
result[key] += amount
return result, added_count
current = {'green': 15, 'blue': 7}
increments = [
('red', 6),
('blue', 17),
('orange', 9)
]
result, count = counting_init(current, increments)
print(result, '/', count)
>>>
{'green': 15, 'blue': 24, 'red': 6, 'orange': 9}) / 2
위의 클로저 함수를 이용하여 초기화 횟수 체크를 하는 방법도 있지만
같은 기능을 하는 클래스를 선언하여 활용 할 수도 있다.
아래 코드는 CountInit 클래스를 정의하고
초기화시에 이를 실행하는 check_init 함수와 해당 함수를 호출한 횟수를 저장하는 added 변수를 선언한다.
from collections import defaultdict
class CountInit:
def __init__(self):
self.added = 0
def check_init(self):
self.added += 1
return 0
current = {'green': 15, 'blue': 7}
increments = [
('red', 6),
('blue', 17),
('orange', 9)
]
counter = CountInit()
result = defaultdict(counter.check_init, current)
for key, amount in increments:
result[key] += amount
print('초기화 횟수 :', counter.added)
>>>
초기화 횟수 : 2
목적을 더욱 명확하게 하기위해 함수 check_init을 __call__로 선언할 수 있다. ( __init__과 같은 목적 )
코드가 좀 더 깔끔해질 수 있고 처음보는 사람은 __call__ 함수를 통해
클래스 흐름의 시작점이 어디인지 쉽게 파악할 수 있다.
__call__ 함수를 실행하면 객체를 함수처럼 호출 할 수 있다.
아래의 예시를 보자
from collections import defaultdict
class CountInit:
def __init__(self):
self.added = 0
def __call__(self): # 변경 코드
self.added += 1
return 0
current = {'green': 15, 'blue': 7}
increments = [
('red', 6),
('blue', 17),
('orange', 9)
]
counter = CountInit()
result = defaultdict(counter, current) # __call__에 의존함
for key, amount in increments:
result[key] += amount
print('초기화 횟수 :', counter.added)
예시를 통해 여러 기능들 사이에 인터페이스가 필요할 때는
클래스(__call__)를 정의 할지 함수(클로저)를 사용할지 그 목적에 맞게
적합한 방법을 선택하면 된다.
참조 : 파이썬 코딩의 기술 서적
'Programming > Python' 카테고리의 다른 글
| Python - Product vs Permutations vs Combinations (0) | 2024.08.18 |
|---|---|
| Python - Numpy란 [1] (0) | 2023.11.18 |
| Python - 파이썬 코딩의 기술을 통한 클린코딩 (0) | 2023.06.11 |
| Python - AES 양방향 암호화 (0) | 2023.03.27 |
| Python - ModuleNotFoundError : No module named (0) | 2023.03.27 |