前言
Python是一种高级程式语言,提供了许多强大的功能和工具,其中包括闭包(closure)和装饰器(decorator)。这些功能可以帮助各位更有效地撰写程式码,并使程式码变得简洁。本文将探讨Python中的闭包和装饰器,并解释它们如何工作以及我如何使用它们。
以上皆为本人开发经验分享,若有更多应用也欢迎提出!
闭包(closure)
闭包是一种函数编程的概念,它是指在内部函数中引用了外部函数的变量,并且外部函数已经返回了内部函数。这种情况下,内部函数将保留对外部函数变量的引用,即使外部函数已经完成执行。这种引用关係就形成了闭包。闭包可以捕获并存储其所引用的变量的值,这些值将在内部函数被调用时使用。这听起来像在绕口令,写起来更像在证明複杂的数学定理QQ,我们直接来看看程式部分是怎么撰写的吧!
首先先来个简单的例子,我们透过这个概念定义一个相加的程式:
def plus_x(x): def plus_y(y): return x + y return plus_yadd_5 = plus_x(5)add_10 = plus_x(10)print(add_5) <function outer_func.<locals>.inner_func at 0x0000028F529F49D0>print(add_10) <function outer_func.<locals>.inner_func at 0x0000028F52B0EAF0>print(add_5(2)) # 7print(add_5(4)) # 9print(add_10(4)) # 14
我们可以看到我们定义了一个相加的程式,add_5 = plus_x(5)
也是一个副程式,这个副程式接受一个输入,并将输入+5后输出,另外我们也定义一个可以+10的副程式。但add_5
本质上还是一个函式,所以直接print的时候会发现它是一个函式物件,那在使用add_5
这个函式的时候会呼叫plus_y(y)
的部分,从而输出最终结果。
以我的理解简单来说,闭包就是让我们可以定义多个功能类似的副程式(本例子是定义+5跟+10的副程式),透过外部程式的参数微调整个副程式的功能,就不用浪费篇幅去写两个类似的功能了。读者也可以建立几个以一个数字为参数输入的普通副程式,并比较看看差异~
装饰器(decorator)
装饰器是一种Python函数,它可以修改另一个函数的行为,而无需修改原本副程式的程式码。装饰器通常被用于在不修改函数的程式码的情况下添加额外的功能或行为(有点像给钢弹换上装备一样)。装饰器可以像普通函数一样被调用,并将另一个函数作为参数进行修改。装饰器通常使用@符号标记在函数的定义之前。
也是一样直接来看範例。今天我想测试每个程式执行的速度,但不希望改到原本副程式的程式码,我们就可以这样写:
from time import timedef timing(func): def wrapper(): start=time() func() #执行被装饰的函数 end=time() print(f"used time: {end-start}") return wrapper@timing #使用装饰器装饰sumfunc()这个副程式def sumfunc(): s=0 for i in range(100000): s+=i print(s)sumfunc()# 4999950000# used time: 0.00461125373840332
首先,装饰器会用到闭包的概念,我们定义了一个计时的副程式timing
,这个外部函式会接受一个函式输入(这很重要),底下实际呼叫sumfunc()
的时候,其实是执行wrapper()
(因为sumfunc()
使用timing
去装饰,新增了一个计时功能,所以实际执行的时候是呼叫timing
这个部分),接着这个外部函式接受的函式输入(也就是sumfunc()
)就会在wrapper()
中被执行,并输出结果。
也因为我们宣告的副程式(sumfunc()
)实际上会在wrapper()
中被执行,所以有一些回传值的处理就要在wrapper()
中处理了。我们改写一下程式:
from time import timedef timing(func): def wrapper(): start=time() result = func() #执行被装饰的函数 end=time() print(f"used time: {end-start}, result:{result}") return wrapper@timing #使用装饰器装饰sumfunc()这个副程式def sumfunc(): s=0 for i in range(100000): s+=i return s sumfunc()# used time: 0.005532264709472656, result:4999950000
我们可以看到,从wrapper()
中取得副程式的结果,最后再print出来。除此之外装饰器也有很多用途。希望各位能好好的活用它~
结语
这两个功能是我最近在开发深度学习的一些专案时,觉得很好用的方法(另外一点是写起来感觉很帅XD)。想藉此机会分享给大家,但小弟我才疏学浅,若有错误也欢迎指正!
接下来没意外我会分享如何在keras中使用这两个概念建立神经网路层(functional api的写法就很类似)。再写生成对抗网路(GAN)这种类型,模型较複杂的程式就可以用到。
另外有一些提问与未详细说明的,我会在另一篇补充更详细的说明喔~
python中的装饰器小补充
这些功能其实较冷门,所以具体在写程式都还是要看自己的习惯喔!