Python装饰器
1. 装饰器
增强函数或类的功能的一个函数,可以装饰函数,也可以装饰类
1.1 举个栗子
def time_decorator(func): #装饰器
def wrapper(*args, **kwargs): #wrapper在第二节中具体讲
# 在函数前打印开始时间
start_time = time.time()
print("start time is:", start_time)
# 执行待装饰函数本身
f = func(*args, **kwargs)
# 打印结束时间和执行耗时
end_time = time.time()
print("end time is:" ,end_time)
exec_time = end_time - start_time
print("execute time is : ", exec_time)
return f
return wrapper
1.2 使用方法
-
方法一:使用语法糖@符号
@time_decorator def add(a,b): print("inside decorator") return a + b @time_decorator def sub(a,b): return a - b print(add(2,3))
-
方法二:常规方法 (!注意传参方式 !)
def add(a,b): print("inside decorator") return a + b print(time_decorator(add)(2, 3))
start time is: 1599904203.3112652 inside decorator end time is: 1599904203.3112652 execute time is : 0.0 5
1.3 参数传递
-
不传参的装饰器
def login(func): def wrapper(*args, **kargs): print("function name is:", func.__name__) return func(*args, **kargs) return wrapper @login def f(): print("in the function f") f()
function name is: f in the function f
-
自身传参的装饰器
def login(text): # 装饰器login传入text参数 def decorator(func): # func函数的装饰器decorator def wrapper(*args, **kargs): print(text + "***" + func.__name__) return func(*args, **kargs) return wrapper return decorator @login("this is the text para") def f(): print("in the function f") f()
this is the text para***f in the function f
2 Wrapper
2.1 可变长度参数*args,**kwargs
- args表示任何多个无名参数,它是一个tuple;kwargs(keyword arguments)表示关键字参数,它是一个dict
同时使用*args和**kwargs时,*args参数必须要列在**kwargs前。
def function(x,y,*args,**kwargs):
print(type(x))
print(args)
print(kwargs)
print(type(args)) #*args返回的是数组
print(type(kwargs)) #**kwargs返回的字典
function(1,2,3,4,5,a=1,b=2,c=3)
<class 'int'>
(3, 4, 5)
{'a': 1, 'b': 2, 'c': 3}
<class 'tuple'>
<class 'dict'>
2.2 @wraps
- 避免被装饰函数自身的信息丢失,不改变使用装饰器原有函数的结构(如__name__, doc)
- def wrapper(func)和@wraps(func) 是一回事
- from functools import wraps @wraps(func)
3 内置装饰器
3.1 @property
-
通过@property装饰后的方法也可以通过一个实例像访问数据属性一样去访问函数,触发一个函数的执行,动态计算出一个值
import math class Circle: def __init__(self, radius): self.radius = radius @property def area(self): return math.pi * self.radius**2 @property def perimeter(self): return 2*math.pi*self.radius # 通过实例访问类中属性 circle = Circle(10) print(circle.radius) # 访问函数 print(circle.area) print(circle.perimeter)
10 314.1592653589793 62.83185307179586
-
property对象还具有setter、deleter 可用作装饰器。setter是设置属性值,deleter用于删除属性值。
-
官方文档中给出getter用于获取属性信息,但实际使用中可直接通过property获取属性信息
class C: def __init__(self): self.x = None @property def x(self): return self.x @x.setter def x(self, value): self.x = value @x.deleter def x(self): del self.x c = C() print(c.x) c.x = 100 print(c.x) del c.x #删除属性
None 100
3.2 @staticmethod
-
修饰的方法不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等
-
除了@staticmethod装饰后的,python类方法都需要self参数
class A(): number = 10 @classmethod def get_a(cls): #cls 接收的是当前类,类在使用时会将自身传入到类方法的第一个参数 print("This is class itself:", cls) print("This is class's attribute:", cls.number) class B(A): number = 20 A.get_a() B.get_a()
This is class itself: <class '__main__.A'> This is class's attribute: 10 This is class itself: <class '__main__.B'> This is class's attribute: 20
3.3 @classmethod
-
改变一个方法为静态方法,静态方法不需要传递隐性的第一参数,静态方法的本质类型就是一个函数,一个静态方法可以直接通过类进行调用,也可以通过实例进行调用
import time class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day @staticmethod def now(): t = time.localtime() return Date(t.tm_year, t.tm_mon, t.tm_mday) @staticmethod def tomorrow(): t = time.localtime(time.time()+86400) return Date(t.tm_year, t.tm_mon, t.tm_mday) a = Date(2005, 11, 27) print(a.year, a.month, a.day) b = Date.now() print(b.year, b.month, b.day) c = Date.tomorrow() print(c.year, c.month, c.day)
2005 11 27 2020 9 12 2020 9 13
3.4 区分一哈
通俗且不咋子准确的概括一下就是:
- @property可以通过实例来调用函数,@staticmethod无需实例化直接调就完事
- 访问类属性或调用实例方法时,@staticmethod需要把类名写上,@classmethod就是传一个类的参数cls代表本类然后直接用
官方一点来讲:
- The difference between a static method and a class method is:
Staticmethod knows nothing about the class and just deals with the parameters
Classmethod works with the class since its parameter is always the class itself.
!总结 !
- 不需要用到与类相关的属性与方法时,用静态方法
@staticmethod
- 要用到与类相关的属性与方法,且想表明这个方法是整个类通用的而不是对象特异的,用类方法
@classmethod