>  기사  >  백엔드 개발  >  Python의 클로저와 데코레이터에 대한 자세한 설명

Python의 클로저와 데코레이터에 대한 자세한 설명

零下一度
零下一度원래의
2017-06-25 09:55:031384검색

클로저는 함수형 프로그래밍에서 중요한 문법 구조입니다. 클로저는 코드를 정리하는 구조이기도 하며 코드의 재사용성을 향상시킵니다.

인라인 함수에서 외부 함수의 변수(전역 범위는 아님)가 참조되는 경우 인라인 함수는 클로저로 간주됩니다.

외부 함수 내에서 정의되었지만 내부 함수에서 참조하거나 사용하는 변수를 자유 변수라고 합니다.

요약하자면 클로저 생성은 다음 사항을 충족해야 합니다.

  • 1. 인라인 함수가 있어야 합니다.

  • 2 인라인 함수는 외부 함수의 변수를 참조해야 합니다.

  • 3. 함수의 반환 값은 내장된 함수여야 합니다

1. 클로저 사용 예

먼저 클로저의 예를 살펴보겠습니다.

In [10]: def func(name):
    ...:     def in_func(age):
    ...:         print 'name:',name,'age:',age
    ...:     return in_func
    ...: 

In [11]: demo = func('feiyu')In [12]: demo(19)
name: feiyu age: 19

여기서 func가 호출되면 클로저가 발생합니다. 패키지 - in_func가 생성되고 클로저는 자유 변수 - name을 보유하므로 이는 또한 함수 func의 수명 주기가 종료 후에도 변수 name은 클로저에 의해 참조되므로 여전히 존재하므로 재활용되지 않습니다. func 的时候就产生了一个闭包——in_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误。看以下示例:

实现一个计数闭包的例子:

def counter(start=0):count = [start] def incr():count[0] += 1return countreturn incr

a = counter()
print 'a:',aIn [32]: def counter(start=0):
    ...:     count = start
    ...:     def incr():
    ...:         count += 1
    ...:         return count
    ...:     return incr
    ...: 

In [33]: a = counter()In [35]: a()  #此处会报错

UnboundLocalError: local variable 'count' referenced before assignment

应该像下面这样使用:

In [36]: def counter(start=0):
    ...:     count = [start]
    ...:     def incr():
    ...:         count[0] += 1
    ...:         return count
    ...:     return incr
    ...: 

In [37]: count = counter(5)

In [38]: for i in range(10):
    ...:     print count(),
    ...:     
[6] [7] [8] [9] [10] [11] [12] [13] [14] [15]

2.使用闭包的陷阱

In [1]: def create():
   ...:     return [lambda x:i*x for i in range(5)]  #推导式生成一个匿名函数的列表
   ...: 

In [2]: create()Out[2]: 
[<function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>]In [4]: for mul in create():
   ...:     print mul(2)
   ...:     
88888

结果是不是很奇怪,这算是闭包使用中的一个陷阱吧!来看看为什么?

在上面的代码当中,函数create返回一个list里面保存了4个函数变量,这4个函数都共同的引用了循环变量i, 也就是说它们共享着同一个变量ii是会改变的,当函数调用时,循环变量i已经是等于4了,因此4个函数返回的都是8。如果,需要在闭包使用循环变量的值的话,把循环变量作为闭包的默认参数或者是通过偏函数来实现。实现的原理也很简单,就是当把循环变量当参数传入函数时,会申请新的内存。示例代码如下:

In [5]: def create():
   ...:         return [lambda x,i=i:i*x for i in range(5)] 
   ...: 
In [7]: for mul in create():
   ...:     print mul(2)
   ...:     
02468

3,闭包与装饰器

装饰器就是一种的闭包的应用,只不过其传递的是函数:

def addb(func):def wrapper():return &#39;<b>&#39; + func() + &#39;</b>&#39;return wrapperdef addli(func):def wrapper():return &#39;<li>&#39; + func() + &#39;</li>&#39;return wrapper 

@addb         # 等同于 demo = addb(addli(demo)) 
@addli        # 等同于 demo = addli(demo)def demo():return &#39;hello world&#39;

print demo()    # 执行的是 addb(addku(demo))

在执行时,首先将demo函数传递给addli进行装饰,然后将装饰后的函数传递给addb进行装饰。所以最后返回的结果是:

<b><li>hello world</li></b>

4.装饰器中的陷阱

当你写了一个装饰器作用在某个函数上,这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都会丢失。

def out_func(func):def wrapper():
        func()return wrapper@out_funcdef demo():"""
        this is  a demo.
    """print &#39;hello world.&#39;if __name__ == &#39;__main__&#39;:
    demo()print "__name__:",demo.__name__print "__doc__:",demo.__doc__

看结果:

hello world.__name__: wrapper__doc__: None

函数名字和文档字符串都变成了闭包的信息。好在可以使用 functools 库中的 @wraps

python 함수에서는 외부 변수를 직접 참조할 수 있지만 외부 변수를 다시 작성할 수는 없습니다. 따라서 클로저에서 상위 함수의 변수를 직접 다시 작성하면 오류가 발생합니다. 다음 예를 보세요:

계산 클로저 구현 예: 🎜
from functools import wrapsdef out_func(func):    @wraps(func)def wrapper():
        func()return wrapper
🎜는 다음과 같이 사용해야 합니다. 🎜rrreee🎜2. 클로저 사용의 함정🎜rrreee🎜결과가 매우 이상하지 않나요? 폐쇄 트랩 it! 왜 그런지 볼까요? 🎜🎜위 코드에서 create 함수는 4개의 함수 변수가 포함된 list를 반환합니다. 이 4개의 함수는 모두 루프 변수 i 를 참조합니다. 즉, 동일한 변수 i를 공유하고 i가 변경됩니다. 함수가 호출되면 루프 변수 i > 이미 4와 같으므로 4개 함수 모두 8을 반환합니다. 클로저에서 루프 변수의 값을 사용해야 하는 경우 루프 변수를 클로저의 기본 매개변수로 사용하거나 부분 함수를 통해 구현하세요. 구현 원리도 매우 간단합니다. 즉, 루프 변수가 매개변수로 함수에 전달되면 새 메모리가 적용됩니다. 샘플 코드는 다음과 같습니다. 🎜rrreee🎜3, 클로저 및 데코레이터 🎜🎜데코레이터는 일종의 클로저 애플리케이션이지만 함수를 전달합니다. 🎜rrreee🎜실행 시 먼저 데모 코드> 함수는 장식을 위해 <code>addli에 전달되고, 장식된 함수는 장식을 위해 addb에 전달됩니다. 따라서 반환된 최종 결과는 다음과 같습니다.🎜rrreee🎜4. 데코레이터의 트랩🎜🎜함수에 대해 작동하는 데코레이터를 작성하면 이름, 문서 문자열, 주석 및 매개변수와 같은 이 함수의 중요한 메타 정보가 손실됩니다. . 🎜rrreee🎜결과를 보세요: 🎜rrreee🎜함수 이름과 문서 문자열이 클로저 정보가 되었습니다. 다행히 functools 라이브러리의 @wraps 데코레이터를 사용하여 기본 래퍼 함수에 주석을 달 수 있습니다. 🎜rrreee🎜결과를 직접 시험해 보세요! 🎜

위 내용은 Python의 클로저와 데코레이터에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.