1. 介绍
在Python
中,装饰器是一种高级语言特性,它可以用于修改现有函数或类的行为,在不改动这个函数的前提下,扩展这个函数的功能。比如计算函数耗时、给函数加缓存等。
2. 内置装饰器
2.1 @classmethod
Python
不像其他语言,可以在类中通过关键字声明静态方法,而是通过装饰器@classmethod
来实现,具体实例如下:
# -----------------------类文件:demo/myClass.py ----------
class Book:
bookName: str = "西游记"
# 使用装饰器
@classmethod
def echoBookName(cls):
print("书名: {} ".format(cls.bookName))
# ------------------------- 运行 -------------------------
from demo.myClass import Book
if __name__ == '__main__':
Book.echoBookName()
# ------------------------- 输出 -------------------------
书名: 西游记
@注: 带装饰类的方法,会隐式的将类当做第一个参数,传递给方法,调用时无须实例化。
2.2 @staticmethod
@staticmethod
也是代表的静态方法,与@classmethod
区别是,它和类没有绑定,不能像上个示例一样直接访问类的属性:
# -----------------------类文件:demo/myClass.py ----------
class Book:
...
@staticmethod
def echoPrice():
print("书的价格: 32.89 ")
# ------------------------- 运行 -------------------------
from demo.myClass import Book
if __name__ == '__main__':
Book.echoPrice()
# ------------------------- 输出 -------------------------
书的价格: 32.89
下面演示,尝试访问类属性:
# -----------------------类文件:demo/myClass.py ----------
class Book:
...
# 这里使用的非类静态方法装饰
@staticmethod
def echoBookName2(cls):
print("书名: {} ".format(cls.bookName))
# ------------------------- 运行 -------------------------
from demo.myClass import Book
if __name__ == '__main__':
Book.echoBookName2()
# ------------------------- 输出 -------------------------
Traceback (most recent call last):
File "D:\wwwroot\python\test\demo\myClass.py", line 47, in <module>
Book.echoBookName2()
TypeError: Book.echoBookName2() missing 1 required positional argument: 'cls'
2.3 @property
这个装饰器的功能,可以让我们像访问类属性一样,访问方法。比如访问stu.info()
,可以写成stu.info
,而不用带括号。如下示例:
# -----------------------类文件:demo/myClass.py ----------
class Student:
name: str = ""
age: int = 18
def __init__(self, name: str, age: int):
self.name = name
self.age = age
@property
def info(self):
return "Student name:{} age:{}".format(self.name, self.age)
# ------------------------- 运行 -------------------------
from demo.myClass import Student
if __name__ == '__main__':
stu = Student("张三", 20)
print("stu.info: ", stu.info)
# ------------------------- 输出 -------------------------
stu.info: Student name:张三 age:20
@property
也可以和deleter、getter、setter
结合一起使用,下面是示例是和setter
结合,对私有属性的封装;
# -----------------------类文件:demo/myClass.py ----------
class Account:
__money: float = 0.00
@property
def money(self):
return self.__money
# 设置金额
@money.setter
def money(self, money: float):
self.__money = money
# ------------------------- 运行 -------------------------
from demo.myClass import Account
if __name__ == '__main__':
a = Account()
print("设置前-金额: ", a.money)
# 设置金额
a.money = 100.99
print("设置后-金额: ", a.money)
# ------------------------- 输出 -------------------------
设置前-金额: 0.0
设置后-金额: 100.99
# ------------------------- 不设置@money.setter时 -------------------------
Traceback (most recent call last):
File "D:\wwwroot\python\test\main.py", line 7, in <module>
a.money = 100.99
AttributeError: can't set attribute 'money'
2.4 @wrapper
当使用装饰器之后,原函数的一些信息会被装饰器函数覆盖,为了解决这类问题,可以使用@wrapper
来修复这个问题,@wrapper在模块functools下,用之前先导入.
不使用@wrapper示例:
# ------------------------- 函数定义 -------------------------
# 定义装饰器
def wrapperDemo(func):
def execFunc(*args, **kw):
res = func(*args, **kw)
return res
return execFunc
# 使用函数装饰器
@wrapperDemo
def test():
print("test func run ok")
# ------------------------- 运行 -------------------------
if __name__ == '__main__':
print("执行函数名称: ", test.__name__)
# ------------------------- 输出-------------------------
执行函数名称: execFunc
通过上面示例可以看出,虽然我们打印的是
test.__name__
,希望输出的是test
,结果却是:execFunc
使用@wrapper示例:
只需要改写装饰器函数,加上@functools.wraps(func)
# ------------------------- 函数定义 -------------------------
# 先导入这个模块
import functools
# 修改装饰器
def noWrapper(func):
## 这里加上wraps
@functools.wraps(func)
def execFunc(*args, **kw):
res = func(*args, **kw)
return res
return execFunc
...
# ------------------------- 输出-------------------------
执行函数名称: test
3 自定义装饰器(无参)
3.1 定义语法
# func指被装饰的函数
def 装饰器名称(func):
# 加上wraps,防止装饰器副作用
@functools.wraps(func)
# tmpFunc是临时函数名,可以自定义
def tmpFunc(*args, **kw):
# 函数执行前处理......
res = func(*args, **kw)
# 函数执行后处理......
return res
return tmpFunc
3.2 使用示例
# ------------------------- 函数定义 -------------------------
# 定义函数装饰器
def useTime(func):
@functools.wraps(func)
def execFunc(*args, **kw):
# 定义开始时间
beginTime = time.time()
print("函数执行前: ", beginTime)
res = func(*args, **kw)
# 计算耗时
ut = time.time() - beginTime
print("函数耗时: %s 秒" % int(ut))
return res
return execFunc
# 使用函数装饰器
@useTime
def test():
time.sleep(3)
print("test func run ok")
# ------------------------- 运行 -------------------------
if __name__ == '__main__':
test()
# ------------------------- 输出 -------------------------
函数执行前: 1692287796.785186
test func run ok
函数耗时: 3 秒
4.自定义装饰器(有参)
4.1 定义语法
def 装饰器名称(arg):
# ---下面这层,可以理解是整个无参数层---
def tmpfunc(func):
@functools.wraps(func)
def execFunc(*args, **kw):
res = func(*args, **kw)
return newRes
return execFunc
return tmpfunc
4.2 使用示例
# ------------------------- 函数定义 -------------------------
def haveArgs(arg):
def tmp(func):
@functools.wraps(func)
def execFunc(*args, **kw):
res = func(*args, **kw)
newRes = "arg:{} | {}".format(arg, res)
return newRes
return execFunc
return tmp
# 使用函数装饰器
@haveArgs("hello world")
def test():
return "test func run ok"
# ------------------------- 运行 -------------------------
if __name__ == '__main__':
r = test()
print("r: ", r)
# ------------------------- 输出 -------------------------
r: arg:hello world | test func run ok
5. 自定义类装饰器
# ------------------------- 定义装饰器:demo/cacheClass.py -------------------------
import functools
class Cache:
@classmethod
def setCache(cls, cacheKey):
def tmpFunc(func):
@functools.wraps(func)
def do(*args, **kw):
res = func(*args, **kw)
print("设置缓存>>>> key:{} value:{}".format(cacheKey, res))
return res
return do
return tmpFunc
# ------------------------- 运行 -------------------------
from demo.cacheClass import Cache
# 使用函数装饰器
@Cache.setCache(cacheKey="add_key_compute")
def compute(a: int, b: int) -> int:
return a + b
if __name__ == '__main__':
r = compute(1, 9)
print("r: ", r)
# ------------------------- 输出 -------------------------
设置缓存>>>> key:add_key_compute value:10
r: 10
网友评论