

클로저(Closure) :
어떤 함수의 내부 함수가 외부 함수의 변수를 참조할 때, 외부 함수가 실행을 마친 후에도 내부 함수가 외부 함수의 변수를 참조할 수 있도록 값을 어딘가에 저장하는 함수이다.
클로저의 구성요소
1. 외부 함수
2. 외부 함수 내에 정의된 내부 함수
3. 내부 함수가 외부 함수의 변수를 참조
4. 외부 함수가 내부 함수를 반환
예제
def outer_function(a):
x = a + 2
def inner_function(y):
return x + y
return inner_function
# outer_function 호출 및 정상 종료
closure_instance = outer_function(10)
# 종료된 outer_function의 변수 x 사용
result = closure_instance(5) # 결과: 17
위 예제에서 inner_function은 클로저로, outer_function의 변수 x를 캡처하여 사용한다.
outer_function는 정상적으로 호출 및 종료되었지만, 다음 줄 closure_instance에서 변수 x를 사용하여 결과를 반환한다.
원래의 함수라면 정상적으로 종료된 함수는 메모리에서도 삭제되지만, 중첩함수인 inner_function가 외부 함수 outer_function의 변수 x를 참조하기에, 변수 x와 inner_function을 저장하는 클로저가 동적으로 생성되었고, closure_instance가 실행될 때 해당 클로저를 참조하여 결과값을 반환할 수 있다.
def outer_function(a):
x = a + 2
def inner_function(y):
return x + y
return inner_function
def normal_function(a):
return a + 2
# outer_function 호출 및 정상 종료
closure_instance = outer_function(10)
# 종료된 outer_function의 변수 x 사용
result = closure_instance(5)
print(result)# 결과: 17
# 클로저 함수의 dir
print(dir(closure_instance)) # ['__annotations__', '__call__', '__class__', '__closure__', '__code__',...
# 일반 함수의 dir
print(dir(normal_function)) # ['__annotations__', '__call__', '__class__', '__closure__', '__code__',...
파이썬의 모든 함수객체는 __closure__ 속성을 가지고 있다.
두 함수객체의 Closure 내부 속성을 살펴보자.
def outer_function(a):
x = a + 2
def inner_function(y):
return x + y
return inner_function
def normal_function(a):
return a + 2
# outer_function 호출 및 정상 종료
closure_instance = outer_function(10)
# 종료된 outer_function의 변수 x 사용
result = closure_instance(5)
print(result)# 결과: 17
# 클로저 함수의 dir
print(closure_instance.__closure__[0].cell_contents) # 결과: 12
try:
# 일반 함수의 dir
print(normal_function.__closure__[0].cell_contents)
except Exception as e:
# 오류출력
print('Error Message:', e) # 결과: Error Message: 'NoneType' object is not subscriptable
여기서 클로저와 일반 함수의 차이를 볼 수 있다.
클로저함수의 경우 내부 __closure__[0].cell_contents 속성에 외부 변수를 저장한다.
하지만 일반 함수의 경우 closure 속성의 내부가 비어있으므로(NoneType) 오류가 발생한다.
클로저의 장점
데이터를 캡슐화 하여 외부에서의 직접접근을 막을 수 있다.
또한, 인터페이스를 일치시키기 위한 방법으로 쓰일 수 있다.
예시는 아래와 같다.
클로저 사용
def get_collate_fn(pad_index):
def collate_fn(batch):
### MY CODE
code = pad_index
return batch
return collate_fn
def get_data_loader(dataset, batch_size, pad_index, shuffle=False):
collate_fn = get_collate_fn(pad_index)
data_loader = torch.utils.data.DataLoader(
dataset=dataset,
batch_size=batch_size,
collate_fn=collate_fn,
shuffle=shuffle,
)
return data_loader
클로저 미사용
def collate_fn(batch, pad_index):
### MY CODE
code = pad_index
return batch
def get_data_loader(dataset, batch_size, pad_index, shuffle=False):
data_loader = torch.utils.data.DataLoader(
dataset=dataset,
batch_size=batch_size,
collate_fn=lambda batch: collate_fn(batch, pad_index),
shuffle=shuffle,
)
return data_loader
단일 인자(batch)만을 제공하는 torch.utils.data.DataLoader에서 collate_fn이 단일 인자(batch)만 받는 함수일 것으로 예상하게 해줄 수 있다.
불필요한 lambda 함수의 사용을 줄이면서도, 추가 인자(이 경우 pad_index)를 collate_fn에 전달할 수 있게 한다.
위의 get_data_loader함수와 클로저를 이해할때, 연관된 커링의 개념을 이해하여 좀 더 쉽게 이해할 수 있다.
Currying(커링)
def curry_multiply(x):
def multiply_by_y(y):
def multiply_by_z(z):
return x * y * z
return multiply_by_z
return multiply_by_y
# 사용 예시
curried_multiply = curry_multiply(2)(3)(4)
print(curried_multiply) # 출력: 24
# 단계별 적용
step1 = curry_multiply(2)
step2 = step1(3)
step3 = step2(4)
print(step3) # 출력: 24
이렇게 인자를 하나씩 넣어주는 것을 함수형 프로그래밍에서 커링(Currying)이라고 한다.
커링은 클로저의 특성을 활용하여 구현된다.
커링된 함수의 각 단계는 클로저를 형성하여 이전 단계의 인자를 기억한다. 이를 통해 함수의 일부 인자를 미리 고정하고 나머지 인자를 나중에 전달할 수 있습니다.
'Language > Python' 카테고리의 다른 글
Python에서 .env 환경변수 파일 작성 및 관리 (0) | 2024.10.31 |
---|---|
Lazy Evaluation : 파이썬의 객체지향 (1) | 2024.08.22 |
개발새발라이프
hi there🙌