您的位置:首页 > 编程语言 > Python开发

Python闭包和装饰器

2018-01-29 23:23 369 查看
In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record
storing a function together with an environment: a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.
A closure—unlike a plain function—allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope.

上面引用自维基百科,闭包就是词法闭包或者函数闭包,闭包是存储着函数和一个环境的记录,涉及到函数的自由变量的映像,这些变量在局部使用,但是被定义在一个闭包范围内。当闭包被创建,值和引用的名字就会被绑定。闭包即使在函数被调用的范围之外,仍然可以通过他们的值或者引用的闭包副本来捕获变量。

代码如下:

def A(num_a):
def B(num_b):
print('in b')
print(num_a + ' ' + num_b)
return B

结果如下:



此处,调用了函数A,返回的是函数B,函数B就是一个闭包,调用了函数A时传递的参数num_a就是自由变量,当函数A的生命周期结束之后,num_a这个变量依然存在,它被闭包所引用,不会被Python解释器所回收。Python2中只能读取外部函数的变量,不能改写它,Python3中的nonlocal关键字,只要在闭包内用nonlocal声明变量,就可以让解释器在外层函数中查找变量名,nonlocal语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。

装饰器就是对原来的函数增加额外功能并且实现代码的重用,而且不需要改动原来的代码。

代码如下:

def a(func):  #装饰器函数
def b():
print('do something before')
func()
print('do something after')
return b

def c():  # 被装饰的函数
print('yes, i am')

c = a(c)
c()

使用装饰器语法糖改写:
def a(func):
def b():
print('do something before')
func()
print('do something after')
return b

@a
def c():
print('yes, i am')

c()

运行结果:



装饰器的语法糖是一个@, 后跟一个装饰函数, 当然也可以是一个装饰类, 另起一行是定义需要被装饰的函数,函数c在被函数a进行装饰后, 返回函数b, 此时现在的函数c相当于闭包函数b, 因此在此处执行函数c后,相当于打印before语句, 执行原来函数c,再打印after语句

以上是最简单的对无参数的函数进行装饰, 以下是对有参数的函数进行装饰

代码如下:

def a(func):
def b(name):
print('before')
func(name)
print('after')
return b
@a
def say_hello_name(name):
    print('hello, %s' % name)
运行结果:



此处相当于将say_hello_name的函数名传递给装饰器函数a的func参数, func函数相当于一个自由变量, 现在的被装饰的函数相当于闭包函数b, 因此say_hello_name里的name参数相当于映射到闭包函数b中的name参数, 而且say_hello_name函数和b函数的参数只要个数对应即可,函数参数并不要求绝对名称相同, 例如定义say_hello_name(sb_name), 闭包函数b中的参数仍然是name,也可以。个人认为可以理解成实参到形参的映射关系,
闭包函数b中的name相当于形参, 而say_hello_name中的name参数相当于实参, 当然只是个人观点

当参数不确定时, 对参数进行传递, 在函数形参部分使用*args和**kwargs, 其实它们也可以看作一个参数, 只不过将实参部分给定的任意个参数转换成一个元素或者字典而已。

传递参数不确定时, 代码如下:

def a(func):
def b(*args, **kwargs):
print('do something before')
func(*args, **kwargs)
print('do something after')
return b

@a
def _sum(*args, **kwargs):
num_sum = 0
for i in args:
num_sum += i
print('The result is %s' %num_sum)
运行结果:



此时可以看到向_sum函数传递多个参数值来做加法运算, 所有的参数其实传递给了*agrs这个元组, 此实例中**kwargs的字典中不包含任何元素, 因此在_sum(1, 2, 3)传递实参到闭包函数b中,形参args为(1, 2, 3)的元组, kwargs则为{}空字典, 所有实参中的值会传入到args的元组中, 而以键值对的方式则传入到kwargs字典中

为装饰器传递参数

def a(name):
def b(func):
print('decoration\' name is %s' % name)
print('do something before')
func()
print('do something after')
return b

@a('deco1')
def say_hello_haoran():
print('hello, haoran')
运行结果:



其实为装饰器传递参数, 可以看成将被装饰的函数的第一个参数传递给装饰函数, 当装饰器后不跟参数时, 则相当于将函数名作为第一个参数传递给装饰函数。此处, 若不给装饰器后的函数a传递参数的话, 那么say_hello_haoran函数将会作为实参传递给定义的装饰函数a的形参name, 而后面的形参func则没有接受到对应的实参, 就会出错。

为装饰器传递类参数

class author():
@staticmethod
def name():
print('author\'s name is haoran')
@staticmethod
def age():
print('author\'s age is 22')
@staticmethod
def sex():
print('author\'s is a real man')
def a(cls):
    def b(func):
        cls.name()
        cls.age()
        cls.sex()
        print('do something before')
        func()
        print('do something after')
    return b

@a(author)
def say_hello_haoran():
    print('hello, haoran')
运行结果:



其实, 此处仍然可以将装饰后的say_hello_haoran函数看成是闭包函数b。

以上只是自己的对于Python装饰器的浅薄认识, 欢迎各位前辈指出错误, 谢谢, 嘿嘿。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  闭包 装饰器