设为首页 加入收藏

TOP

python闭包和装饰器(一)
2019-02-17 12:07:49 】 浏览:116
Tags:python 包和 装饰

闭包是一种函数,从形式上来说是函数内部定义(嵌套)函数,实现函数的扩展。在开发过程中,考虑到兼容性和耦合度问题,如果想在原有的函数基础上添加东西而又不改动原有函数的结构,通常会使用闭包。但闭包的功能还不只是这个。实际上,闭包会保留定义函数时存在的自由变量的绑定,这样在调用函数时,虽然定义作用域不可用了,但是仍然可以使用那些绑定的变量。简单来说,普通函数在调用完后,函数内部的变量就释放了(因为直接调用的函数没有绑定在某一个对象上,Cpython解析器就会把它回收了),而闭包内部的变量仍然保存着。

 

普通函数不会保存变量的值:

例如:
def function(value):
    nums = []
    nums.append(value)
    return nums

func = function(2)
func2 = function(3)
print(func)     # [2]
print(func2)    # [3]
两次调用函数返回的值都是不相同的

 

在闭包中,外部函数的变量一直会为内部函数“保留”着,每次调用函数都可以获取这些变量

def closure():
    nums =[]
    def function(value):
        nums.append(value)
        return nums
    return function

close = closure()
close(5)
close(6)
close(7)
# 元组形式返回嵌套函数function的变量
print(close.__code__.co_varnames)       #('value',)
print(close.__code__.co_freevars)        #('nums',)
# 列表形式保存嵌套函数中自由变量的值
print(close.__closure__[0].cell_contents)      #[5, 6, 7]

闭包的执行顺序可以理解为:

closure(function(value))

因此close = closure()相当于为闭包创建了一个绑定的对象,这个对象内部有变量nums和函数function。当变量在下一次调用的时候,这些量还会保存着。因此当多次调用close()的时候,nums列表会更新。

 

以上代码的解析:

对象审查(反射)

_ _code_ _返回对象中的函数

_ _co_varnames 返回内部函数中保存的变量,例如function中的value

_ _co_freenames 返回内部函数中的自由变量,自由变量是编程中的一个专业名词。

如上面的代码中,nums并不是在function函数中绑定的,它是在它的外部函数的作用域范围内绑定的,所以在function内部,nums是一个自由变量。而close对象为function函数保留了这个自由变量,在每次调用函数时,都可以更新这个自由变量。

自由变量(所有的)实际保存在闭包中,可以通过_ _closure_ _来获取,它是一个列表,每个元素都表示一个自由变量,如上面的nums。每个元素都是一个cell,它的属性cell_contents保存着这个自由变量的值,因此有:

_ _closure_ _[0].cell_contents

 

更多关于函数/类/生成器的审查可以参考官方文档:

https://docs.python.org/2/library/inspect.html

 

 

一、函数形式的装饰器

上面讲了如何通过嵌套函数实现一个闭包,下面将装饰器是如何实现的。实际上,装饰器离不开对闭包的理解,函数形式的装饰器看起来像是闭包换了一种表达形式,调用起来更灵活和更方便。

例如:

# 函数形式的闭包
registry = []

def decorator(func):
    print('registe %s'%func)
    registry.append(func)
    return func              # 返回的量必须是一个函数,否则会报错

@decorator
def fun1():
    print('running fun1')

@decorator
def fun2():
    print('running fun2')

@decorator
def fun3():
    print('running fun3')

fun1()
fun2()
fun3()

 

结果:

registe <function fun1 at 0x000002234D891378>
registe <function fun2 at 0x000002234D891400>
registe <function fun3 at 0x000002234D891488>
running fun1
running fun2
running fun3

装饰器看起来有点像闭包,只不过是加了一个@的外壳,而这个外壳函数的参数必须是一个函数,并且必须要有返回函数(返回的一般是内部函数)

装饰器的执行顺序:

decorator(func)

 

内部函数的参数可以在函数调用时传入,而不必像闭包那样必须由对象传入。

值得注意的是:装饰器函数有导入时运行和运行时运行的区别,装饰器在模块导入的时候就执行了,而被装饰的函数则在调用的时候才执行。

 

上面这个被装饰器“装饰”的函数似乎看起来跟装饰器“互动”很少,那么下面结合装饰器和闭包实现一个更复杂的装饰器:

def decorator(func):
    def outerFunc(*args):      # 装饰器内部实现闭包,闭包的外部函数接受任意定位变量
        outerFunName = outerFunc.__name__
        innerFunName = func.__name__
        print('change innerFunc:%s to outerFunc:%s'%(innerFunName, outerFunName))
        result = func(*args)            # 可以实现,因为闭包中保存了自由变量func
        result += " and start running"
        return result
    return outerFunc          # 将改变返回的函数,返回外部函数

@decorator
def fun(str):
    return str

str = fun('This is funciton1')   # change innerFunc:fun to outerFunc:outerFunc
print(str)                      # This is funciton1 and start running

装饰器执行顺序:

decorator(outerFunc(func(args)))

 

这个装饰器内部的闭包实现还是比较简单的,只是为了说明原理,在编程过程中可以根据实际添加更多的功能实现。

 

继续改造,让装饰器也带上参数

# 带参数装饰器
def decorator(name):
    def _decorator(func):
        def outerFunc(*args):
            print(name)
            outerFunName = outerFunc.__name__
            innerFunName = func.__name__
            print('change
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇禁用 urllib3 的安全请求警告 下一篇#2 判断一个字符串是否包含重复字..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目