python入门精讲-27装饰器
作者:互联网
01. 装饰器的作用
用于管理和增强 函数
和 类
行为的代码,提供一种在函数或者类定义中插入自动运行代码的机制。
特点:提供了更明确的语法,更高的代码可维护性和更好的一致性。
可以通过函数或者类来定义装饰器,可以实现装饰器函数、装饰器类,装饰器函数可以用于修饰函数+类方法,类装饰器也可以用于修饰函数+类方法,但是类作为装饰器,来装饰类会遇到问题,建议使用函数来定义装饰器。
02.装饰器的函数基础
02-1 函数赋值给变量
直接输入函数的名称,返回的是一个地址。可以将函数名赋值给变量,通过变量来执行函数。
02-2函数作为参数传递
函数同样也可以作为参数进行传递,下面的例子表明:可以通过传递函数名来确认要执行的函数,对于没有定义的函数动作,还可以通过lambda进行函数参数的返回。
02-3函数的嵌套
# 返回的是被嵌套函数的执行结果
def func1():
def func2():
return "Hello"
return func2()
r = func1()
print(r)
# 返回的是被嵌套函数的 函数对象
def func3():
def func4():
return "Hello"
return func4
r = func3()
print(r)
运行的结果:
Hello
<function func3.<locals>.func4 at 0x7f97d9c045e0>
返回的是函数对象,如果要返回函数的执行结果,可以直接运行得到。
在上面的基础之上,加上代码:
r = func3()()
print(r)
运行结果:Hello
函数嵌套,返回值,和函数嵌套中定义返回的内容有关。
02-4函数的作用域
LEGB 从内到外,不使用本地的x,打算使用封装的x,使用nonlocal来定义x,意思就是这个变量的值不是本地的变量值,使用和封装的x的变量,如下是从内到外的一个作用域。
L : local
E : enclosure
G: global
B: builtin
同样如果声明变量的值为global 的话,就会使用全局的变量的值。
def func():
x = 10
def func2():
x = 20
return x+10
return func2()
def func3():
x = 10
def func4():
nonlocal x
return x+10
return func4()
print(func())
print(func3())
输出:
30
20
例子2:
x = 500
def f1():
x = 100
def f2():
# 使用封装的x,会改变f1()中的x,global的x不会改变
nonlocal x
print(f'Nonlocal的x:{x}')
x += 20
print(f'函数f2的x:{x}')
def f3():
# 使用全局的x,改变global的x的值
global x
x += 20
print(f'函数f3的global的x的值是{x}')
f2()
print(f'函数f1外侧的x:{x}')
f3()
f1()
print(f'Global x is:{x}')
输出:
Nonlocal的x:100
函数f2的x:120
函数f1外侧的x:120
函数f3的global的x的值是520
Global x is:520
03.函数定义装饰器
03-1 函数装饰器的作用
装饰器用于接收一个函数名的变量作为参数,可以对函数进行装饰:对结果进行处理、添加额外的功能等等操作,装饰器可以批量丰富函数的功能。
03-2函数作为装饰器-装饰函数
带参数的函数的装饰器,装饰器的参数是函数名称,装饰器函数是如何获取到函数的参数呢。
是通过;*args, **kwargs 来获取到的函数的参数。
以下的例子为:带参数 + 不带参数的装饰器的用法,可以看到带参数的函数say_hello('Lucy')可以正常传递。
import time
def wrapperfun(func):
def warpper(*args, **kwargs):
print(func(*args, **kwargs) + '@' + str(time.time()))
return warpper
@wrapperfun
def say_hello(name):
return f'Hello {name}'
@wrapperfun
def say_hi():
return 'Hi'
if __name__ == "__main__":
say_hello("Lucy")
say_hi()
输出:
Hello Lucy@1658300817.562695
Hi@1658300817.562695
- 定义一个装饰器,可以装饰不同的函数,让每个函数执行一些相同的操作
- 带参数的函数,可以通过装饰器内部的
(*args,**kwargs)
来传递,表示传递所有的参数 - 装饰器函数本身,返回的是一个函数对象
03-3函数作为装饰器-装饰类方法
可以使用定义的函数装饰器来修饰类方法,使用的方法和函数基本一致
import time
def wrapperfun(func):
def warpper(*args, **kwargs):
print(func(*args, **kwargs) + '@' + str(time.time()))
return warpper
class Student:
def __init__(self,name):
self.name = name
# 定义的函数装饰器来修饰类方法
@wrapperfun
def get_name(self):
return self.name
if __name__ == "__main__":
s = Student('Lily')
s.get_name()
输出结果:
Lily@1658363063.2143705
例子02:
def wrapper_fun(func):
def wapperf(*args,**kwargs):
return func(*args,**kwargs).upper()
return wapperf
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
# 用于修饰类方法
@wrapper_fun
def get_info(self):
return f'Person info: name-{self.name},age-{self.age}'
if __name__ == '__main__':
p = Person('Mike',33)
print(p.get_info())
输出结果:
PERSON INFO: NAME-MIKE,AGE-33
04.定义类作为装饰器
04-1 类作为装饰器-装饰函数
定义一个类,做为装饰器,用来装饰一个函数。
使用了__call__方法,该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。
class AddTag:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
return '<p>'+self.func(*args,**kwargs)+'</p>'
@AddTag
def greeting(name):
return f'Hi {name}'
if __name__ == "__main__":
print(greeting('Amanda'))
输出:
<p>Hi Amanda</p>
例子2:
class Decorator:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
return self.func(*args,**kwargs).upper()
@Decorator
def greeting(name):
return f'Hello,{name}'
if __name__ == '__main__':
print(greeting('Jim'))
输出:
HELLO,JIM
04-2 类装饰器来装饰-类方法
1、没有使用装饰器
class AddTag:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
return '<p>'+self.func(*args,**kwargs)+'</p>'
class Student:
def __init__(self,name):
self.name = name
def get_name(self):
print(self.name)
if __name__ == "__main__":
s = Student('Lucy')
s.get_name()
返回的结果:Lucy
2、使用了类装饰器
类装饰中使用了__call__方法调用传递过来的函数对象
class AddTag:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
return '<p>'+self.func(*args,**kwargs)+'</p>'
class Student:
def __init__(self,name):
self.name = name
@AddTag
def get_name(self):
print(self.name)
if __name__ == "__main__":
s = Student('Lucy')
s.get_name()
运行的结果:
抛出异常,提示参数的个数不正确,AddTag和get_name中的self字段冲突了
出现该问题的原因:AddTag装饰器类中有self这个参数,同样被修改的函数也有self这个参数,两个相同的参数导致无法区分。
例子:打印函数的耗时
import time
def clock(func):
def wrapper(*args, **kwargs):
print(f'Start time:{time.ctime()}')
func(*args, **kwargs)
print(f'End time:{time.ctime()}')
return wrapper
@clock
def intersect(sq1, sq2):
res = []
for sq in sq1:
if sq in sq2 and sq not in res:
res.append(sq)
else:
print('Task done')
print(f'res is:{res}')
if __name__ == "__main__":
sq1 = [1, 2, 4, 5, 421, 3, 14, 546, 4, 1, 1, 4, 1, 3, 444, 4, 5, 4]
sq2 = [3.1, 3, 11, 32, 54, 1, 3, 544, 4, 4, 1, 55, 1, 1, 4145546, 14]
intersect(sq1, sq2)
# seq1 = set(sq1)
# seq2 = set(sq2)
# sq = seq1&seq2
# print(f'sq is:{sq}')
运行的结果:
05.函数做为装饰器是通用的
定义一个装饰器,尽量使用函数的方式进行定义,函数的方式是通用的。
而通过类定义的装饰器,只能用来装饰函数,不经过其他处理无法装饰类方法。
结论:定义装饰器,尽量使用函数的方式来定义
06.参数化装饰器
在没有使用参数装饰器之前,添加装饰器后,起到修饰作用的参数是固定的,如函数中的
字符,如果想要修饰的参数可变,需要参数化装饰器。
没有使用参数化的装饰器
def add_tag(func):
def wrapper(*args,**kwargs):
return f'<p>{func(*args,**kwargs)}</p>'
return wrapper
@add_tag
def say_hi(name):
return f'Hi {name}'
if __name__ == "__main__":
print(say_hi('Lucy'))
使用参数化的装饰器
可以在之前没有参数化的装饰器上再定义一层函数,然后再加上返回值。
def tag(tag):
def add_tag(func):
def wrapper(*args,**kwargs):
return f'<{tag}>{func(*args,**kwargs)}<{tag}>'
return wrapper
return add_tag
@tag('body')
@tag('p')
@tag('div')
def say_hi(name):
return f'Hi {name}'
if __name__ == "__main__":
print(say_hi('Lucy'))
运行的结果:
<body><p><div>Hi Lucy<div><p><body>例子02 带参数的装饰器
01 - 不带参数
def decorate(func):
def wrapper(*args,**kwargs):
return '<p>' + func(*args,**kwargs) + '</p>'
return wrapper
@decorate
def greeting(name):
return f'Hello ,{name}'
if __name__ == '__main__':
print(greeting('Tom'))
输出:
<p>Hello ,Tom</p>
02 - 带参数
如果装饰器带参数,就要在之前的装饰器上再封装一层即可,并逐层进行return
先写好一个不带参数的装饰器decorate,然后再加上一层函数tags并传递一个参数,tags函数返回decorate
并且一个函数,还可以被多个装饰器装饰,执行的顺序是从内到外的顺序,如下代码所示:先使用tags('p')进行装饰,然后再使用tags('div')进行装饰。
def tags(tag):
def decorate(func):
def wrapper(*args,**kwargs):
return f'<{tag}>{func(*args,**kwargs)}</{tag}>'
return wrapper
return decorate
@tags('div')
@tags('p')
def greeting(name):
return f'Hello ,{name}'
if __name__ == '__main__':
print(greeting('Tom'))
标签:__,27,return,name,python,精讲,装饰,def,函数 来源: https://www.cnblogs.com/spring2022/p/16501167.html