Python中的闭包

当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。

1 def make_printer(msg):
2    def printer():
3        print(msg)  # 夹带私货(外部变量)
4    return printer  # 返回的是函数,带私货的函数
5 printer = make_printer('Foo!')
6 printer()

同一个的函数夹带了不同的私货,就实现了不同的功能。

 1 def tag(tag_name):
 2     def add_tag(content):
 3         return "<{0}>{1}</{0}>".format(tag_name, content)
 4 
 5     return add_tag
 6 
 7 
 8 content = 'Hello'
 9 
10 add_tag = tag('a')
11 print(add_tag(content))
12 # <a>Hello</a>
13 
14 add_tag = tag('b')
15 print(add_tag(content))
16 # <b>Hello</b>

Python中的装饰器Decorator,假如你需要写一个带参数的装饰器,那么一般都会生成闭包。

 1 # how to define
 2 def wrapper(func1):  # 必须接受一个且仅一个函数作为参数
 3    return func1  # 返回一个且仅一个callable对象,一般为函数
 4 
 5 # how to use
 6 def target_func(args):# 目标函数
 7    pass
 8 
 9 # 调用方式一,直接包裹
10 result = wrapper(target_func)(args)
11 
12 # 调用方式二,使用@语法,等同于方式一
13 @wrapper
14 def target_func(args):
15    pass
16 
17 result = target_func()

装饰器如果带参数呢?那么就需要在原来的装饰器上再包一层,用于接收这些参数。这些参数(私货)传递到内层的装饰器里后,闭包就形成了。所以说当装饰器需要自定义参数时,一般都会形成闭包。(类装饰器例外)

 1 def html_tags(tag_name):
 2    def wrapper_(func):
 3        def wrapper(*args, **kwargs):
 4            content = func(*args, **kwargs)
 5            return "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)
 6        return wrapper
 7    return wrapper_
 8 
 9 @html_tags('b')
10 def hello(name='Toby'):
11    return'Hello {}!'.format(name)
12 
13 # 不用@的写法如下
14 # hello = html_tag('b')(hello)
15 # html_tag('b') 是一个闭包,它接受一个函数,并返回一个函数
16 
17 print(hello())  # <b>Hello Toby!</b>
18 print(hello('world'))  # <b>Hello world!</b>

其实闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的cell对象,每个cell对象一一保存了这个闭包中所有的外部变量。

 1 >>> def make_printer(msg1, msg2):
 2         def printer():
 3             print(msg1, msg2)
 4         return printer
 5 >>> printer = make_printer('Foo', 'Bar')  # 形成闭包
 6 
 7 >>> printer.__closure__   # 返回cell元组
 8 (<cell at 0x03A10930: str object at 0x039DA218>, <cell at 0x03A10910: str object at 0x039DA488>)
 9 
10 >>> printer.__closure__[0].cell_contents  # 第一个外部变量
11 'Foo'
12 >>> printer.__closure__[1].cell_contents  # 第二个外部变量
13 'Bar'

 

posted @ 2018-04-13 21:02  banshaohuan  阅读(104)  评论(0)    收藏  举报