Python中的闭包并不是一个马上就能理解的概念,但是随着学习的深入,无论如何都需要去理解这样一个东西。闭包的概念让我们尝试从概念上理解闭包。在某些语言中,当一个函数可以(嵌套)定义另一个函数时,如果内部函数引用外部函数的变量,则可能会产生闭包。闭包可用于在函数和一组“私有”变量之间创建关系。这些私有变量能够在对给定函数的多次调用中保持它们的持久性。——维基百科)用比较通俗易懂的人的话来说,函数作为对象返回时,包含了外部变量,形成了闭包。参见示例。defmake_printer(msg):defprinter():printmsg#包含私人商品(外部变量)returnprinter#返回一个函数,带有私有商品打印机的函数=make_printer('foo!')printer('foo!')printer('foo!')支持将函数用作对象语言的编程一般都支持闭包。比如Python、JavaScript。如何理解闭包的含义?为什么需要关闭?个人认为闭包的意义在于包含了外部变量(私有物品)。如果不包含私人物品,则与普通功能相同。这没有任何区别。同一个功能包含不同的私人物品,实现不同的功能。其实你也可以这样理解。闭包的概念与面向接口编程的概念非常相似。你可以把闭包理解为一种轻量级的接口封装。接口为方法签名定义了一组绑定规则。deftag(tag_name):defadd_tag(content):return"<{0}>{1}{0}>".format(tag_name,content)returnadd_tagcontent='Hello'add_tag=tag('a')printadd_tag(content)#Helloadd_tag=tag('b')printadd_tag(content)#Hello这个例子中,我们想要一个给content添加标签的函数,但是具体tag_name是什么样子的根据实际需要而定。对外调用的接口已经确定,就是add_tag(content)。如果是面向接口的方式实现,我们会先把add_tag写成一个接口,指定它的参数和返回类型,然后分别实现a和b的add_tag。但是在闭包的概念中,add_tag是一个需要tag_name和content两个参数的函数,只不过参数tag_name被打包带走了。所以你可以一开始就告诉我怎么打包,然后带走就行了。上面的例子不是很形象。其实在我们的生活和工作中,闭包的概念也很常见。比如拨打手机,你只关心你想给谁打电话,而不关心每个品牌的手机是怎么实现的,使用了哪些模块。再比如你去饭店吃饭,只要付钱就可以享受服务,而你不知道那一桌饭菜用了多少地沟油。这些可以看作是闭包,返回了一些功能或服务(打电话,就餐),但是这些功能使用了外部变量(天线,地沟油等)。您也可以将类实例视为闭包。当你构造这个类时,你使用不同的参数。这些参数是闭包中的包。这个类提供的方法就是闭包的函数。但是类比闭包要大得多,因为闭包只是一个可以执行的函数,而类实例可能会提供很多方法。什么时候使用闭包其实闭包在Python中是很常见的,只是作为闭包你并没有特别注意这个。比如Python中的装饰器Decorator,如果需要写一个带参数的装饰器,那么一般都会生成一个闭包。为什么?因为Python的装饰器是固定形式的函数式接口。它要求你的装饰器函数(或装饰器类)必须接受一个函数并返回一个函数:#howtodefinedefwrapper(func1):#Acceptacallableobjectreturnfunc2#Returnanobject,generallyafunction#howtosedeftarget_func(args):#TargetFunctionpass#调用方法1,直接wrapresult=wrapper(target_func)(args)#调用方法2,使用@语法,相当于方法1@wrapperdeftarget_func(args):passresult=target_func()那么如果你的装饰器有参数呢?然后你需要在原来的装饰器上再包裹一层来接收这些参数。这些参数(私有物品)传递给内部装饰器后,闭包就形成了。所以当你的装饰器需要自定义参数的时候,一般都会形成一个闭包。(类装饰器除外)defhtml_tags(tag_name):defwrapper_(func):defwrapper(*args,**kwargs):content=func(*args,**kwargs)return"<{tag}>{content}{tag}>".format(tag=tag_name,content=content)returnwrapperreturnwrapper_@html_tags('b')defhello(name='Toby'):return'Hello{}!'.format(name)#没有@的写法是紧接着#hello=html_tag('b')(hello)#html_tag('b')是一个接受一个函数并返回一个函数的闭包printhello()#HelloToby!printhello('world')#Helloworld!关于装饰器更深入的分析,可以看我写的另一篇博客。再深一点其实也不需要太深。理解上面的概念,很多看似头疼的代码,无非就是这些。让我们看看闭包是什么样子的。其实闭包函数相比普通函数会多一个__closure__属性,它定义了一个元组来存储所有的cell对象,每个cell对象将所有的外部变量一一保存在闭包中。>>>defmake_printer(msg1,msg2):defprinter():printmsg1,msg2returnprinter>>>printer=make_printer('Foo','Bar')#形成一个闭包>>>printer.__closure__#返回单元格元组(
