python装饰器之@classmethod,@staticmethod,@property
一,何为装饰器
官方文档解释
返回值为另一个函数的函数,通常使用
@wrapper
语法形式来进行函数变换。 装饰器的常见例子包括classmethod()
和staticmethod()
。装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:
def f(...): ... f = staticmethod(f) @staticmethod def f(...): ...
二,使用装饰器
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
1,@classmethod
官方文档摘要
@
classmethod
一个类方法把类自己作为第一个实参,就像一个实例方法把实例自己作为第一个实参。请用以下习惯来声明类方法:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
@classmethod
这样的形式称为函数的 decorator -- 详情参阅 函数定义。类方法的调用可以在类上进行 (例如
C.f()
) 也可以在实际上进行 (例如C().f()
)。 其所属类以外的类实例会被忽略。 如果类方法在其所属类的派生类上调用,则该派生类对象会被作为隐含的第一个参数被传入。类方法与 C++ 或 Java 中的静态方法不同。 如果你需要后者,请参阅
staticmethod()
。
使用场景
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) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,但是结果为 ''' 输出结果: <__main__.Date object at 0x1013f9d68> '''
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) @classmethod #改成类方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 ''' 输出结果: year:2017 month:3 day:3 '''
2,@staticmethod
@
staticmethod
将方法转换为静态方法。
静态方法不会接收隐式的第一个参数。要声明一个静态方法,请使用此语法
class C: @staticmethod def f(arg1, arg2, ...): ...
@staticmethod
这样的形式称为函数的 decorator -- 详情参阅 函数定义。静态方法的调用可以在类上进行 (例如
C.f()
) 也可以在实例上进行 (例如C().f()
)。Python中的静态方法与Java或C ++中的静态方法类似。另请参阅
classmethod()
,用于创建备用类构造函数的变体。像所有装饰器一样,也可以像常规函数一样调用
staticmethod
,并对其结果执行某些操作。比如某些情况下需要从类主体引用函数并且您希望避免自动转换为实例方法。对于这些情况,请使用此语法:class C: builtin_open = staticmethod(open)
应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个init函数,此时静态方法就派上用场了
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day)
3,@property
官方文档摘要
class
property
(fget=None, fset=None, fdel=None, doc=None)返回 property 属性。
fget 是获取属性值的函数。 fset 是用于设置属性值的函数。 fdel 是用于删除属性值的函数。并且 doc 为属性对象创建文档字符串。
一个典型的用法是定义一个托管属性
x
:class C: def __init__(self): self._x = None def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x x = property(getx, setx, delx, "I'm the 'x' property.")
如果 c 是 C 的实例,
c.x
将调用getter,c.x = value
将调用setter,del c.x
将调用deleter。如果给出,doc 将成为该 property 属性的文档字符串。 否则该 property 将拷贝 fget 的文档字符串(如果存在)。 这令使用
property()
作为 decorator 来创建只读的特征属性可以很容易地实现:class Parrot: def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage
以上
@property
装饰器会将voltage()
方法转化为一个具有相同名称的只读属性的 "getter",并将 voltage 的文档字符串设置为 "Get the current voltage."特征属性对象具有
getter
,setter
以及deleter
方法,它们可用作装饰器来创建该特征属性的副本,并将相应的访问函数设为所装饰的函数。 这最好是用一个例子来解释:class C: def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x
上述代码与第一个例子完全等价。 注意一定要给附加函数与原始的特征属性相同的名称 (在本例中为
x
。)返回的特征属性对象同样具有与构造器参数相对应的属性
fget
,fset
和fdel
。
属性方法,主要作用是将一个操作方法封装成一个属性,用户用起来就和操作普通属性完全一致,非常简单.定义时,在实例方法的基础上添加@property装饰器,并且只有一个self参数,调用时,不需要括号
@property 是经典类中的一种装饰器,新式类中具有三种:
1.@property获取属性
2.@方法名.setter 修改属性
3.@方法名.deleter 删除属性