python:装饰器
一、函数装饰器
1.装饰器本身不带参数:采用两层函数定义装饰器(外层函数起到传入被修饰函数的效果,里层函数起到在被修饰函数前后增加功能的效果)
def print_func_name(func):
'''
定义装饰器:打印函数名称
'''
def wrapper(*args,**kwargs):
print("装饰器进入")
print(f'装饰器功能显示使用装饰器函数的函数名:{func.__name__}')
#这里可以把传入函数的调用结果存入到变量
#等函数后加入的代码执行后再把结果返回
func_res=func(*args,**kwargs)
print("装饰器结束")
return func_res
return wrapper
@print_func_name
def hellomm(str):
print(f'我是{str}')
str="函数属性"
return str
print(hellomm('函数本尊'))
#结果:
装饰器进入
装饰器功能显示使用装饰器函数的函数名:hellomm
我是函数本尊
装饰器结束
函数属性
2.装饰器本身自带传入参数的装饰器:采用三层函数定义装饰器(在不带参装饰器外层嵌套函数起到一个传入参数的效果)
def print_before_func(str):
def decorator(func):
def wrapper(*args,**kwargs):
print(f'装饰器:我是{str}')
return func(*args,**kwargs)
return wrapper
return decorator
@print_before_func('装饰器')
def printname(str):
print(f'函数:我是{str}')
printname('函数本身')
#结果:
装饰器:我是装饰器
函数:我是函数本身
ddt中的装饰器写法片段:
FILE_ATTR = '%file_path' # store the path to JSON file
YAML_LOADER_ATTR = '%yaml_loader' # store custom yaml loader for serialization
def file_data(value, yaml_loader=None):
def wrapper(func):
setattr(func, FILE_ATTR, value)
if yaml_loader:
setattr(func, YAML_LOADER_ATTR, yaml_loader)
return func
return wrapper
3.wraps装饰器的使用
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、name、参数列表,看例子:
#查看f函数的名称和文档
def f(x):
"""does some math"""
return x + x * x
print(f.__name__)
print(f.__doc__)
#结果:
f
does some math
#查看使用装饰器后f函数的名称和文档
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__)
print(f.__doc__)
#结果:
with_logging
None
不难发现,f函数名称变成with_logging了,f函数的文档注释docstring也找不到了。
这个问题就比较严重的,好在我们有wraps装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(f"被修饰的函数:{func.__name__}")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f"函数名:{f.__name__}") # prints 'f'
print(f"函数注释文档:{f.__doc__}") # prints 'does some math'
print(f(5))
#结果:
函数名:f
函数注释文档:does some math
被修饰的函数:f
30
二、类装饰器
1.类装饰器添加或修改类的属性内容
def Decorator(obj):
'''
实现一个类装饰器
'''
#添加属性
obj.age = "18岁"#给类添加一个age属性
obj.gender="男"#给类添加一个gender属性
def eat():
#给类添加一个eat函数
print("正在吃苹果")
return "苹果吃完了"
obj.eat = eat#使用函数名添加一个函数属性
return obj#返回对象
@Decorator #相当于执行 School = Decorator(School)
class Human():
def __init__(self, name, gender):
self.name =name
self.gender =gender
#打印类的属性字典
print(f"Human类的属性:{Human.__dict__}")
human=Human("倩倩","女")
print(f"调用Human类中的性别属性:{Human.gender}")
print(f"human实例的属性:{human.__dict__}")
print(f"调用human实例的属性:{human.name,human.gender,human.age}")
print(f"访问Human的eat函数:{Human.eat()}")
print(f"访问human的eat函数:{human.eat}")
#结果:
Human类的属性:{'__module__': '__main__', '__init__': <function Human.__init__ at 0x000001C4CEADB670>,
'__dict__': <attribute '__dict__' of 'Human' objects>, '__weakref__': <attribute '__weakref__' of 'Human' objects>,
'__doc__': None, 'age': '18岁', 'gender': '男', 'eat': <function Decorator.<locals>.eat at 0x000001C4CEADBCA0>}
调用Human类中的性别属性:男
human实例的属性:{'name': '倩倩', 'gender': '女'}
调用human实例的属性:('倩倩', '女', '18岁')
正在吃苹果
访问Human的eat函数:苹果吃完了
访问human的eat函数:<bound method Decorator.<locals>.eat of <__main__.Human object at 0x000001E40771B4C0>>
需要注意到:
1.类的属性与实例的属性是互不相干的(创建实例后,Human.gender依然是男;通过human实例调用eat函数,human.eat()会报错,使用human.eat显示绑定装饰器方法)
类可以直接调用属性,只有实例化后才能调用方法,然而上面这里实例化后是没有eat()属性的,所以human.eat()报错,但是这里实例化后实例是没有这个方法的(因为Human类中没有写)
2.实例可以访问类的属性,同名的情况下优先访问实例的属性(human.age的结果是18)
2.通过参数给类添加属性
类似于上面带参的函数装饰器,这里也只需要在类装饰器外面嵌套一层用于接收参数
def Decorator(**kwargs):
def add(obj):
"添加数据属性"
# print('调用外部函数的**kwargs',kwargs)
for key,val in kwargs.items():
# 添加数据属性
setattr(obj,key,val)
return obj
# print("外部传入的参数为:",kwargs)
return add
#执行顺序:1.运行Decorator函数,先打印外部的传入的参数,返回add函数名;2.再执行School = add(School1)
@Decorator(addr = "浙江省杭州市",name ="浙江大学")
class School1():
def __init__(self,price):
self.price =price
@Decorator(addr = "湖北省",price =12000)
class School2():
pass
print(f"类School1的属性:\n{School1.__dict__}")
print(f"类School2的属性:\n{School2.__dict__}")
结果:
类School1的属性:
{'__module__': '__main__', '__init__': <function School1.__init__ at 0x000001EA4EC3BE50>,
'__dict__': <attribute '__dict__' of 'School1' objects>, '__weakref__': <attribute '__weakref__' of 'School1' objects>,
'__doc__': None, 'addr': '浙江省杭州市', 'name': '浙江大学'}
类School2的属性:
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'School2' objects>,
'__weakref__': <attribute '__weakref__' of 'School2' objects>, '__doc__': None, 'addr': '湖北省', 'price': 12000}
可以看到属性已经添加成功
我们常见的内置装饰器有:@property、@staticmethod、@classmethod可以自行补充相关知识
参考:
https://www.zhihu.com/question/26930016
https://blog.51cto.com/10836356/2112490

浙公网安备 33010602011771号