21.python中的闭包和装饰器

  python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

  以下说明主要针对 python2.7,其他版本可能存在差异。

  也许直接看定义并不太能明白,下面我们先来看一下什么叫做内部函数:

def wai_hanshu(canshu_1):

    def nei_hanshu(canshu_2):  # 我在函数内部有定义了一个函数
        return canshu_1*canshu_2

    return nei_hanshu   # 我将内部函数返回出去

a = wai_hanshu(123)     # 此时 canshu_1 = 123
print a
print a(321)    # canshu_2 = 321

  我在函数里面有嵌套了一个函数,当我向外层函数传递一变量的之后,并赋值给 a ,我们发现 a 变成了一个函数对象,而我再次为这个函数对象传参的时候,又获得了内部函数的返回值。我们知道,按照作用域的原则来说,我们在全局作用域是不能访问局部作用域的。但是,这里通过讨巧的方法访问到了内部函数。。

  下面我们继续看一个例子:

def wai_hanshu():
    a = []
    def nei_hanshu(canshu):
        a.append(canshu)
        return a

    return nei_hanshu

a = wai_hanshu()
print a(123)
print a(321)

  可以看出函数位于外部函数中的列表 a 竟然改变了。要知道为什么,就要先知道什么是python的命名空间,而命名空间就是作用域表现的原因,这里我简要说明一下。

  引入命名空间的主要原因还是为了避免变量冲突,因为python中的模块众多,模块中又有函数,类等,它们都要使用到变量。但如果每次都要注意不和其他变量名冲突,那就太麻烦了,开发人员应该专注于自己的问题,而不是考虑别人写的程序中用到了什么变量,所以python引入了命名空间。命名空间分为模块层,模块内又分为全局作用域和局部作用域,用一个图来表示的话:

  模块之间命名空间不同,而里面还有全局作用域和局部作用域,局部作用域之前还能嵌套,这样就能保证变量名不冲突了。这里顺便补充一下,可以通过 __name__ 属性获取命名空间的名字:

  主文件的命名空间是叫做 ‘__main__‘,而模块的命名空间就是模块名。

  作用域的诞生,是因为当python在寻找一个变量的时候,首先会在当前的命名空间中寻找,如果当前命名空间中没有,就到上一级的命名空间中找,以此类推,如果最后都没找到,则触发变量没找到的异常。

  我们之前一直说:全局作用域无法访问局部作用域,而局部作用域能够访问全局作用域就这这个原因。而当我在局部作用域创建了一个和外面同名的变量时,python在找这个变量的时候首先会在当前作用域中找,找到了,就不继续往上一级找了。

  在早期的python版本时,局部作用域是不能访问其他的局部作用域的,只能访问全局的,而现在的版本都是依次向上一级找,这里就提一下。

  也就是因为这个特性,我们可以在内部函数中访问外部函数中的变量,这也就是所谓的闭包了。

  注意:这里要做好对象之间的区分,例如:

def wai_hanshu():
    a = []
    def nei_hanshu(canshu):
        a.append(canshu)
        return a

    return nei_hanshu

a = wai_hanshu()    # 我创建了一个对象
b = wai_hanshu()    # 我又创建了一个对象
print a
print b
print a(123)
print b(321)

  在这里,我们虽然都是操作 wai_hanshu 中的变量,但是 a 和 b 完全是两个对象,它们所在的内存空间也是不同的,所以里面的数据也是独立的。要注意不要搞混。


装饰器

  其实装饰器就是在闭包的基础上多进行了几步,看代码:

def zsq(func):  # 装饰函数
    def nei():
        print ‘我在传入的函数执行之前做一些操作‘
        func()  # 执行函数
        print ‘我在目标函数执行后再做一些事情‘
    return nei

def login():    # 被装饰函数
    print ‘我进行了登录功能‘

login = zsq(login)   # 我将被装饰的函数传入装饰函数中,并覆盖了原函数的入口

login()     # 此时执行的就是被装饰后的函数了

  在看这段代码的时候,要知道几件事:

  1.函数的参数传递的其实是引用,而不是值。

  2.函数名也是一个变量,所以可以重新赋值。

  3.赋值操作的时候,先执行等号右边的。

  只有明白了上面这些事之后,再结合一下代码,应该就能明白什么是装饰器了。所谓装饰器就是在闭包的基础上传递了一个函数,然后覆盖原来函数的执行入口,以后调用这个函数的时候,就可以额外实现一些功能了。装饰器的存在主要是为了不修改原函数的代码,也不修改其他调用这个函数的代码,就能实现功能的拓展。

  而python觉得让你每次都进行重命名操作实在太不方便,于是就给出了一个便利的写法:

def zsq(func):
    def nei():
        print ‘我在传入的函数执行之前做一些操作‘
        func()  # 执行函数
        print ‘我在目标函数执行后再做一些事情‘
    return nei

@zsq    # 自动将其下面的函数作为参数传到装饰函数中去
def login():
    print ‘我进行了登录功能‘

login()

  

  这些小便利也叫做python的语法糖,你可能在很多地方见过这个说法。

带参数的装饰器:

def zsq(a):
    print ‘我是装饰器的参数‘, a
    def nei(func):
        print ‘我在传入的函数执行之前做一些操作‘
        func()  # 执行函数
        print ‘我在目标函数执行后再做一些事情‘
    return nei

@zsq(‘123‘)
def login():
    print ‘我进行了登录功能‘

  相当于: login = zsq(123)(login) ,所以在这里没有调用就执行了。

装饰器的嵌套:

  这里就不完整写个例子了:

@deco1(deco_arg)
@deco2
def func():
    pass

  相当于: func = deco1(deco_arg)(deco2(func))

  也就是从上到下的嵌套了。



  关于闭包和装饰器就先讲到这里,以后有需要再补充。

时间: 2024-10-28 11:45:03

21.python中的闭包和装饰器的相关文章

python中的闭包和装饰器

闭包函数介绍 什么是闭包 维基百科中关于闭包的概念: 在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包.闭包可以用来在一个函数与一组 "私有" 变量之间创建关联关系.在给定函数被多次调用的过程中,这些私有变量能够保持其持久性. 对上面这段话总结一下,即python中的闭包需要满足3个条件:1) 内嵌函数,即函数里定义了函数 -- 这对应函数之间的嵌套2) 内嵌函数必须引用定义在外部函数里中的变量(不是全局作用域中的引用)-- 内部

轻松理解python中的闭包和装饰器 (下)

在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函数,例如filter, map, reduce这些函数 可以定义一个函数作为高阶函数例如: def func(x, y, f): return f(x)+f(y) 可以这样调用func(2,-1,abs) 函数返回结果为3 有些时候,我们不需要显式地定义传入的函数,直接传入匿名函数更方便. 在Pyt

聊聊Python中的闭包和装饰器

1. 闭包 首先我们明确一下函数的引用,如下所示: def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函数 ret = test1 print(id(ret)) print(id(test1)) #通过引用调用函数 ret() 运行结果: --- in test1 func---- 140212571149040 140212571149040 --- in test1 func---- 以y=kx+b为例,请

python中的无参装饰器和有参装饰器

python中的无参装饰器和有参装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 装饰器特点: 1>.开放封闭原则,即对扩展是开放的,对修改时封闭的: 2>.装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用对象: 3>.装饰器的功能是在不修改被装饰器对象源代码以及被装饰器对象的调用方式的前提下为其扩展新功能: 4>.装饰器本质是函数,(即装饰其他函数)就是为其他函数添加附加功能. 一.典型装饰器案例 1 #!/usr/bin/env pyth

Python中,关于@property装饰器

1.为什么使用@property装饰器?br/>在类中,当我么不想在外界直接调用到类的属性,或者不想展示属性的真实内容时,可以用到@property.它规定了我们直接用 对象名.属性名 获取对象属性时并不会直接取得对象的属性,而是通过调用@property装饰过的属性函数来给调用者反馈. 2.我们为什么不使用特定的方法来进行上面的操作?原因是因为太繁琐.例: class Person: def __init__(self, username, password) -> None: self.u

Python基础day-7[闭包,装饰器]

闭包: 首先说下闭包是什么? 闭包就是在函数内部定义的函数,包含对外部作用域的引用,但不包含全局作用域.因为函数的作用域在定义的时候就固定死了,所以闭包函数有自带作用域和延迟计算的特点. 闭包函数定义:如果一个内部函数,包含了对外部作用域的引用,但是不是包含全局作用域.那么这个函数就被认为是闭包函数.闭包函数可以使用".__closure__" 来查看闭包函数的属性.下面我们来看一个示例: def t(): money = 100 def s(): print(money) retur

Python中带参数的装饰器

装饰器本身是用来是为一个函数是实现新的功能,并且不改变原函数的代码以及调用方式. 遇到这样一种问题: 众多函数调用了你写的装饰器,但客户有需求说,我想实现我可以随之控制装饰器是否生效. 那你就不可能在得到命令的时候去原函数头部去做删除和添加装饰器调用的命令.这是就可以用到带参数的装饰器,定义一个开关,调用装饰器的时候,把这个装饰器的开关参数给传递进去,这样当开关打开的时候装饰器生效,关闭的时候则只执行原函数的代码. 举例:开关参数为True的时候执行过程: 1 F = True #step 1

python函数2_闭包和装饰器

变量的范围(局部/全局) 局部变量 在函数内部声明的变量 在函数体外部无法获取 全局变量 在函数外部声明的变量 所有函数都可以访问 在函数内,局部变量和全局变量同名,优先使用局部变量 name = '月月' def fun2(): name = '小月月' name += '会弹吉他' print(name) fun2() 小月月会弹吉他 当在函数体内,尝试更改全局变量会报错 name = '月月' def fun2(): print(name) name += '会弹吉他' fun2() 当需

13、python中的函数(闭包与装饰器)

一.嵌套函数 函数的内部又再定义另一个函数,这个函数就叫嵌套函数,里面含函数就叫内部函数. 示例: 二.返回函数 函数可以接收函数对象作为参数,同理函数也能返回一个函数对象作为返回值. 示例: 返回函数可以用来延迟函数的执行. 三.命名空间与变量作用域 变量作用域指的是变量的存活的范围.命名空间指的是属于一个对象的所有属性(对象)的集合. 示例: A的命名空间是A函数对象里面的所有对象的集合,包括变量a.函数B.变量b:B的命名空间就是属于函数B的所有对象的集合,包括变量b: a的变量作用域就是