Python의 클로저
요 며칠 전에 누군가가 메시지를 남겼는데, 闭包
과 re.sub
중 어느 하나의 용도가 명확하지 않습니다. Script Home을 검색해 보니 클로저와 관련된 내용을 작성한 적이 없어서 Python의 내용을 요약하고 개선하기로 결정했습니다.
1. 폐쇄의 개념
우선 클로저란 무엇인가에 대한 기본 개념부터 시작해야 합니다. Wiki의 설명을 살펴보겠습니다.
위에 언급된 두 가지 핵심 사항은 자유 변수와 함수입니다. 이 두 가지 키에 대해서는 나중에 설명하겠습니다. "클로저"의 의미에 대해서는 아직 자세히 설명할 필요가 있습니다. 텍스트에서 알 수 있듯이 닫힌 패키지로 생생하게 이해할 수 있습니다. 물론 함수 내부에도 해당 논리가 있습니다. 패키지 내부는 자유입니다. 자유 변수는 패키지와 함께 돌아다닐 수 있습니다. 물론, 이 패키지가 생성된다는 전제가 있어야 합니다.
예:
def func(name): def inner_func(age): print 'name:', name, 'age:', age return inner_func bb = func('the5fire') bb(26) # >>> name: the5fire age: 26
여기에서 func가 호출되면 클로저 - inner_func가 생성되고 클로저는 자유 변수인 name을 보유합니다. 따라서 이는 함수 func의 수명 주기가 끝날 때 변수 이름이 참조되기 때문에 Exists로 유지된다는 의미이기도 합니다. 폐쇄되므로 재활용되지 않습니다.
한 가지 더, 폐쇄는 Python에서 고유한 개념이 아닙니다. 함수를 일급 시민으로 취급하는 모든 언어에는 폐쇄라는 개념이 있습니다. 그러나 클래스가 일급 시민인 Java와 같은 언어에서도 클로저를 사용할 수 있지만 클래스나 인터페이스를 사용하여 구현해야 합니다.
자세한 개념정보는 마지막 참고링크를 참고해주세요.
2. 클로저를 사용하는 이유
위의 소개를 토대로 독자들은 이것이 클래스와 다소 비슷하다고 느낄지 궁금합니다. 유사점은 둘 다 데이터 캡슐화를 제공한다는 것입니다. 차이점은 클로저 자체가 메소드라는 점입니다. 클래스와 마찬가지로 우리는 공통 기능을 재사용하기 위해 프로그래밍할 때(물론 실제 비즈니스를 모델링할 때에도) 일반적인 것을 클래스로 추상화하는 경우가 많습니다. 클로저의 경우에도 마찬가지입니다. 함수별로 세분화된 추상화가 필요한 경우 클로저가 좋은 선택입니다.
이 시점에서 클로저는 읽기 전용 객체로 이해할 수 있습니다. 클로저에 속성을 전달할 수 있지만 실행 인터페이스만 제공할 수 있습니다. 따라서 프로그램에서는 나중에 언급할 데코레이터와 같은 공통 기능을 완성하는 데 도움이 되는 함수 객체(클로저)가 종종 필요합니다.
3. 클로저 사용
Python에서 매우 중요하고 일반적인 사용 시나리오인 첫 번째 시나리오는 데코레이터입니다. Python은 데코레이터를 위한 매우 친숙한 "구문 설탕"을 제공합니다. 이를 통해 장식을 매우 편리하게 사용할 수 있습니다. 간단히 말해서, @ decorator_func를 func 함수에 추가하면 decorator_func(func):
와 동일합니다.def decorator_func(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @decorator_func def func(name): print 'my name is', name # 等价于 decorator_func(func)
이 데코레이터 예에서 클로저(래퍼)는 외부 func 매개변수를 보유하고 외부에서 전달된 매개변수를 허용할 수 있습니다. 수신된 매개변수는 그대로 func에 전달되어 실행 결과로 반환됩니다.
이것은 간단한 예입니다. 자주 사용되는 LRUCache 데코레이터와 같이 여러 개의 클로저를 가질 수 있습니다. 데코레이터는 @lru_cache(expire=500)와 같은 매개변수를 허용할 수 있습니다. 구현은 두 개의 클로저를 중첩하는 것입니다:
def lru_cache(expire=5): # 默认5s超时 def func_wrapper(func): def inner(*args, **kwargs): # cache 处理 bala bala bala return func(*args, **kwargs) return inner return func_wrapper @lru_cache(expire=10*60) def get(request, pk) # 省略具体代码 return response()
클로저에 대해 잘 모르는 학생들은 위의 코드를 이해할 수 있어야 합니다. 이전 인터뷰에서 자주 받았던 질문입니다.
두 번째 시나리오는 클로저의 특징인 "지연 평가"를 기반으로 합니다. 이 애플리케이션은 데이터베이스에 액세스할 때 더 일반적입니다. 예:
# 伪代码示意 class QuerySet(object): def __init__(self, sql): self.sql = sql self.db = Mysql.connect().corsor() # 伪代码 def __call__(self): return db.execute(self.sql) def query(sql): return QuerySet(sql) result = query("select name from user_app") if time > now: print result # 这时才执行数据库访问
위의 부적절한 예는 클로저를 통한 지연 평가 기능을 보여 주지만 위 쿼리에서 반환된 결과는 함수가 아니라 함수 함수를 포함하는 클래스입니다. 관심이 있다면 Django의 쿼리셋 구현을 살펴보세요. 원리는 비슷합니다.
세 번째 시나리오는 특정 함수의 매개변수를 미리 할당해야 하는 상황입니다. 물론 Python에는 functools.parial에 액세스하는 좋은 솔루션이 이미 있지만 클로저를 사용하여 달성할 수도 있습니다.
def partial(**outer_kwargs): def wrapper(func): def inner(*args, **kwargs): for k, v in outer_kwargs.items(): kwargs[k] = v return func(*args, **kwargs) return inner return wrapper @partial(age=15) def say(name=None, age=None): print name, age say(name="the5fire") # 当然用functools比这个简单多了 # 只需要: functools.partial(say, age=15)(name='the5fire')
또 다른 억지스러운 예인 것 같지만 클로저 적용을 실천한 것이라고 볼 수 있습니다.
마지막으로 클로저는 이해하기 쉽고 Python에서 널리 사용됩니다. 이 글은 클로저에 대한 요약입니다. 궁금한 점이 있으면 메시지를 남겨주세요.
4. 참고자료
위키피디아-폐쇄
http://stackoverflow.com/questions/4020419/closures-in-python
http://www.shutupandship.com/2012/01/python-closures-explained.html
http://stackoverflow.com/questions/141642/what-limitations-have-closures-in-python-compared-to-언어-x-closures
http://mrevelle.blogspot.com/2006/10/closure-on-closures.html