Python闭包和装饰器

https://www.toutiao.com/a6683435981611205124/

 

2019-04-24 20:25:01

一、python函数作用域LEGB

python解释器查找变量的原则(顺序):

L→E→G→B

L:Local函数内部作用域

E:enclosing函数内部与内嵌函数之间

G:gobal全局作用域

B:build-in内置作用域

example:

value1 = 5
def my_func():
 value2 = 6
 print(id(value2))
 def in_func():
 a = max(value1, value2)
 print(a)
 return in_func

上面示例代码中max函数为python的内建函数,在in_func函数中,max这个变量python解释器首先会在in_func这个函数中查找,即local函数内部作用域中查找,然后按顺序再到E-G-B查找,最后在build-in内置作用域查找到。

二、闭包

1、概念:内部函数中对enclosing作用域的变量进行引用

上述例子中my_func函数中的内部函数in_func引用了enclosing作用域中的value2变量,这就叫做闭包。

由于函数执行完后变量会被回收,当执行完my_func这个函数后,value2变量会被回收。那么如果我们再次调用in_func这个函数时,需用引用到value2这个变量,是否会报错?

看以下代码:

example:

value1 = 5
def my_func():
 value2 = 6
 print(id(value2)) #打印value2的ID值
 def in_func():
 a = max(value1, value2)
 print(a)
 return in_func
f = my_func() #my_func返回的是in_func,此时f指向in_func函数
f() #f()相当于in_func(),调用了infunc函数
print(f.__closure__)

ouput:

501351024
6
(<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)

如果内部函数引用了enclosing作用域的变量,会将变量添加到函数__closure__的属性中去。当再次查找这个变量时,会直接去函数__closure__的属性中查找。我们可以看到代码的输出结果第一行即为value2的内存地址(501351024转换为16进制:1DE20270)和__closure__属性中的int对象的地址是一样的( int object at 0x000000001DE20270)。

2、那么闭包到底有什么用呢?

我们来看下以下两段代码:

(1)、

def func_100(value):
 passline = 60
 if value >= passline:
 print('pass')
 else:
 print('failed')
def func_150(value):
 passline = 90
 if value >= passline:
 print('pass')
 else:
 print('failed')
func100(59)
func150(89)

(2)、

def set_passline(passline):
 def in_func(value):
 if value >= passline:
 print('pass')
 else:
 print('failed')
 return in_func
f_100 = set_passline(60) #passline=60被存储在f_100的__closure__属性中
f_150 = set_passline(90) #passline=90被存储在f_150的__closure__属性中
f_100(59)
f_150(89)

两段代码都能正确判断满分是100或150时,分数是否及格。但是第二段代码,由于运用闭包,代码复用性更高。

3、闭包更高级的应用

将闭包的概念中的变量变成函数,同样适用。即:内部函数中对enclosing作用域的函数进行引

example:

def my_sum(*args):
 return(sum(args))
def my_average(*args):
 return sum(args)/len(args)
def dec(func):
 def in_dec(*args):
 if len(args) == 0: #对参数进行判断,如果没有参数直接返回0
 return 0
 for i in args:
 if not isinstance(i, int): #对参数进行判断,如果有一个参数不是int类型,直接返回0
 return 0
 return func(*args) #此处对enclosing作用域的func函数进行引用
 return in_dec
evo_my_sum = dec(my_sum)
evo_my_average = dec(my_average)

PS:此处求和函数(my_sum)和求平均值函数(my_average)只对参数是否int类型进行判断。

我们来分析下代码:evo_my_sum = dec(my_sum)

由于函数dec返回的是in_dec函数

所以evo_my_sum指向的是in_dec函数

=》evo_my_sum = in_dec

=》evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)

那么in_dec(1, 2, 3)即evo_my_sum(1, 2, 3)会对求和的参数先进行判断后,再调用my_sum函数。

同样道理:

evo_my_average会对求平均的参数先进行判断后,再调用my_average函数。

这样就可以对参数统一进行判断后再各自调用不同的函数。

三、装饰器

装饰器是用来装饰函数的,它返回一个函数对象。语法:@Decorator

现在我们已经定义了一个my_sum函数

def my_sum(*args):
 return(sum(args))

假设我们要增加my_sum函数的功能,比如,在函数调用前对参数进行一个判断,但又不希望修改my_sum函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

我们要定义一个能判断参数的decorator,如下:

def dec(func):
 def in_dec(*args):
 if len(args) == 0:
 return 0
 for i in args:
 if not isinstance(i, int):
 return 0
 return func(*args)
 return in_dec

按照python装饰器的语法:

@dec
def my_sum(*args):
 return(sum(args))

这样,调用my_sum函数,不仅会运行my_sum函数本身,还会在运行my_sum函数前,对参数进行判断。

把@dec放到my_sum函数的定义处,相当于执行了语句:my_sum = dec(my_sum)看到这里是否觉得有点熟悉?其实这个语句和上述闭包的高级应用是一样的,只不过那部分把my_sum改成了evo_my_sum。

装饰器到这里还差最后一步:my_sum函数经过装饰后,由于dec(my_sum)返回的是in_dec函数,此时my_sum.__name__属性将从‘my_sum’变成‘in_dec’,为了保证此属性不变,需在in_dec函数定义前加上语句@functools.wraps(func),保证my_sum.__name__属性不变。否则,有些依赖函数签名的代码执行就会出错。一个完整的decorator的写法如下:

import functools
def dec(func):
 @functools.wraps(func)
 def in_dec(*args):
 if len(args) == 0:
 return 0
 for i in args:
 if not isinstance(i, int):
 return 0
 return func(*args)
 return in_dec

decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。

python闭包和装饰器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
闭包是一个函数对象,它引用了一些在其定义时外部环境的变量。在Python中,闭包常常用来创建装饰器装饰器其实就是一个闭包,它接受一个函数作为参数,并返回一个替代版函数。闭包允许我们在不修改原始函数的情况下,添加一些额外的功能或逻辑。 一个典型的装饰器的例子可以是这样的:在一个函数外面定义一个装饰器函数,然后通过在要装饰的函数之前添加@装饰器名称的语法糖,来应用装饰器闭包装饰器的实现机制是类似的,都是通过嵌套函数的方式来实现的。在闭包中,内部函数引用了外部函数的变量。而在装饰器中,装饰器函数接受一个函数作为参数,并返回一个内部函数,内部函数可以访问外部函数的变量。 在闭包装饰器的实现过程中,都需要注意作用域的规则。在闭包中,内部函数可以访问外部函数的局部变量,而在装饰器中,装饰器函数可以访问被装饰函数的变量。 闭包装饰器提供了一种灵活的方式来扩展函数的功能,使得我们可以在不修改原始函数的情况下,添加一些额外的逻辑。它们是Python中非常强大而且常用的特性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [python中的闭包装饰器解释](https://blog.csdn.net/qq_39567748/article/details/99596644)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [python高级之装饰器](https://blog.csdn.net/qq_35396496/article/details/109147229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值