Python Notes19:python装饰器

Python Notes19:python装饰器

1. 闭包

要想了解装饰器,首先要了解一个概念,闭包。什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了。光说没有概念,直接上一个例子。
def outer(x): def inner(y): return x + y return inner print(outer(6)(5)) # 输出11
如代码所示,在outer函数内,又定义了一个inner函数,并且inner函数又引用了外部函数outer的变量x,这就是一个闭包了。在输出时,outer(6)(5),第一个括号传进去的值返回inner函数,其实就是返回6 + y,所以再传第二个参数进去,就可以得到返回值,6 + 5。

2. 函数装饰器

其实装饰器就是一个闭包,装饰器是闭包的一种应用。什么是装饰器呢,简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。使用时,再需要的函数前加上@demo即可。

2.1 无参装饰器

# funA 作为装饰器函数 def funA(fn): print("Hello Python") fn() # 执行传入的fn参数 print("人生苦短,我用Python") return "装饰器函数的返回值" @funA def funB(): print("学习 Python") # 📢📢📢这里只能使用funB,与funB()区别 # funB # print(funB()) # 这样报错 print(funB)
输出内容
Hello Python 学习 Python 人生苦短,我用Python 装饰器函数的返回值
被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。
实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。

2.2 带参数的函数装饰器

被修饰的函数本身带有参数,应该如何传值呢?

2.2.1 参数个数一样

比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:
def funA(fn): # 定义一个嵌套函数 def say(arc): print("Python教程:", arc) return say @funA def funB(arc): print("funB():") funB("Hello Python")
运行输出结果
Python教程: Hello Python
这个函数和如下程序是等价的:
def funA(fn): # 定义一个嵌套函数 def say(arc): print("Python教程:",arc) return say def funB(arc): print("funB():",arc) funB = funA(funB) funB("Hello Python")

2.2.2 参数不确定

*args**kwargs 作为装饰器内部嵌套函数的参数,*args **kwargs 表示接受任意数量和类型的参数。
def funA(fn): # 定义一个嵌套函数 def say(*args, **kwargs): fn(*args, **kwargs) return say @funA def funB(arc): print("Python学习:", arc) @funA def other_funB(name, arc): print(name, arc) funB("Hello Python") other_funB("Python教程:", "人生苦短,我用Python")
运行输出结果
Python 学习: Hello Python Python教程: 人生苦短,我用Python

3. 装饰器嵌套

@funA @funB @funC def fun(): #...
执行顺序是由里到外,所以它等效于下面这行代码
fun = funA( funB ( funC (fun) ) )

4. 类装饰器

装饰器也不一定只能用函数来写,也可以使用类装饰器,用法与函数装饰器并没有太大区别,实质是使用了类方法中的call魔法方法来实现类的直接调用。

4.1 不带参数的类装饰器

class logging(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("[DEBUG]: enter {}()".format(self.func.__name__)) return self.func(*args, **kwargs) @logging def hello(a, b, c): print(a, b, c) hello("hello,","good","morning")
运行输出
[DEBUG]: enter hello() hello, good morning

4.2 带参数类装饰器

class logging(object): def __init__(self, level): self.level = level def __call__(self, func): def wrapper(*args, **kwargs): print("[{0}]: enter {1}()".format(self.level, func.__name__)) return func(*args, **kwargs) return wrapper @logging(level="TEST") def hello(a, b, c): print(a, b, c) hello("hello,","good","morning")
运行输出结果
[TEST]: enter hello() hello, good morning