1. 介绍
在Python
中,装饰器是一种高级语言特性,它可以用于修改现有函数或类的行为,在不改动这个函数的前提下,扩展这个函数的功能。比如计算函数耗时、给函数加缓存等。
2. 内置装饰器
2.1 @classmethod
Python
不像其他语言,可以在类中通过关键字声明静态方法,而是通过装饰器@classmethod
来实现,具体实例如下:
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
区别是,它和类没有绑定,不能像上个示例一样直接访问类的属性:
class Book: ... @staticmethod def echoPrice(): print("书的价格: 32.89 ")
from demo.myClass import Book
if __name__ == '__main__': Book.echoPrice()
书的价格: 32.89
|
下面演示,尝试访问类属性:
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 "/Users/liuqh/ProjectItem/PythonItem/fast-use-ai/test/localTest.py", line 47, in <module> Book.echoBookName2() TypeError: Book.echoBookName2() missing 1 required positional argument: 'cls'
|
2.3 @property
这个装饰器的功能,可以让我们像访问类属性一样,访问方法。比如访问stu.info()
,可以写成stu.info
,而不用带括号。如下示例:
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
结合,对私有属性的封装;
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
Traceback (most recent call last): File "/Users/liuqh/ProjectItem/PythonItem/fast-use-ai/test/localTest.py", line 50, in <module> a.money = 100.99 ^^^^^^^ AttributeError: property 'money' of 'Account' object has no setter
|
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): @functools.wraps(func) def execFunc(*args, **kw): res = func(*args, **kw) return res
return execFunc ...
执行函数名称: test
|
3. 自定义装饰器(无参)
3.1 定义语法
def 装饰器名称(func): @functools.wraps(func) 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. 自定义类装饰器
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
|