比较混淆的几个装饰器方法
概述:
1.staticmethod和classmethod通过装饰器装饰@staticmethod和@classmethod装饰后的方法;可可以通过类名直接调用;
classmethod隐藏参数为cls;staticmethod无隐含参数
2.实例方法必须先创建实例,再用实例调用方法;实例方法隐藏参数是实例自身self;当然实例也可以调用类方法和静态方法;
3.属性函数是将方法当作属性来调用;通过实例调用属性的时候,直接执行方法被装饰器@property装饰过的方法。
eg:
静态方法:
是指类中无需实例参与即可调用的方法(不需要self参数),在调用过程中,无需将类实例化,直接在类之后使用.号运算符调用方法。
通常情况下,静态方法使用@staticmethod装饰器来声明。
class ClassA(object):
@staticmethod
def func_a():
print('Hello Python')
if __name__ == '__main__':
ClassA.func_a()
# 也可以使用实例调用,但是不会将实例作为参数传入静态方法
ca = ClassA()
ca.func_a()
类方法:
在Python中使用比较少,类方法传入的第一个参数为cls,是类本身。并且,类方法可以通过类直接调用,或通过实例直接调用。但无论哪种调用方式,最左侧传入的参数一定是类本身。
通常情况下,类方法使用@classmethod装饰器来声明
class ClassA(object):
@classmethod
def func_a(cls):
print(type(cls), cls)
if __name__ == '__main__':
ClassA.func_a()
ca = ClassA()
ca.func_a()
从运行结果可以看出,无论是类调用还是实例调用,类方法都能正常工作。且通过打印cls,可以看出cls传入的都是类实例本身。
如果存在类的继承,那类方法获取的是类树上最底层的类
class BaseA(object):
@classmethod
def func_a(cls):
print(type(cls), cls)
class BaseB(object):
pass
class ClassA(BaseA, BaseB):
pass
if __name__ == '__main__':
ClassA.func_a()
ca = ClassA()
ca.func_a()
代码中ClassA继承自BaseA、BaseB,在调用类方法时,虽然类方法是从BaseA继承而来,但是传入func_a的cls函数实际上是ClassA,也就是最底层(最具体)的类。
所以,在某些时候,需要明确调用类属性时,不要使用类方法传入的cls参数,因为它传入的是类树中最底层的类,不一定符合设计初衷。
可以直接通过类名访问类属性。
class BaseA(object):
@classmethod
def func_a(cls):
# 直接使用类名,而不使用cls
print(BaseA)
print(type(cls), cls)
class BaseB(object):
pass
class ClassA(BaseA, BaseB):
pass
if __name__ == '__main__':
ClassA.func_a()
实例方法,除静态方法与类方法外,类的其他方法都属于实例方法。
实例方法需要将类实例化后调用,如果使用类直接调用实例方法,需要显式地将实例作为参数传入。
最左侧传入的参数self,是实例本身。
class ClassA(object):
def func_a(self):
print('Hello Python')
if __name__ == '__main__':
# 使用实例调用实例方法
ca = ClassA()
ca.func_a()
# 如果使用类直接调用实例方法,需要显式地将实例作为参数传入
ClassA.func_a(ca)
属性函数(property)
一、装饰器
使用属性函数的最简单的方法之一是将它作为一个方法的装饰器来使用。这可以让你将一个类方法转变成一个类属性。当我需要做某些值的合并时,我发现这很有用。其他想要获取它作为方法使用的人,发现在写转换函数时它很有用。
class Person(object):
""""""
#----------------------------------------------------------------------
def __init__(self, first_name, last_name):
"""Constructor"""
self.first_name = first_name
self.last_name = last_name
#----------------------------------------------------------------------
@property
def full_name(self):
"""
Return the full name
"""
return "%s %s" % (self.first_name, self.last_name)
结果:
>>> person = Person("Mike", "Driscoll")
>>> person.full_name
'Mike Driscoll'
>>> person.first_name
'Mike'
>>> person.full_name = "Jackalope"
Traceback (most recent call last):
File "", line 1, in
AttributeError: can't set attribute
正如你所看到的,因为我们将方法变成了属性,我们可以使用正常的点符号访问它。但是,如果我们试图将该属性设为其他值,我们会引发一个AttributeError错误。改变full_name属性的唯一方法是间接这样做:
>>> person.first_name = "Dan"
>>> person.full_name
'Dan Driscoll'
二、使用property替代setter和getter方法
from decimal import Decimal
########################################################################
class Fees(object):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self._fee = None
#----------------------------------------------------------------------
def get_fee(self):
"""
Return the current fee
"""
return self._fee
#----------------------------------------------------------------------
def set_fee(self, value):
"""
Set the fee
"""
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value
使用这个类,我们必须要定义getter和setter方法
>>> f = Fees()
>>> f.set_fee("1")
>>> f.get_fee()
Decimal('1')
如果你想添加可以使用正常点符号访问的属性,而不破坏所有依赖于这段代码的应用程序,你可以通过添加一个属性函数非常简单地改变它:
from decimal import Decimal
########################################################################
class Fees(object):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self._fee = None
#----------------------------------------------------------------------
def get_fee(self):
"""
Return the current fee
"""
return self._fee
#----------------------------------------------------------------------
def set_fee(self, value):
"""
Set the fee
"""
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value
fee = property(get_fee, set_fee)
末尾添加一行代码,现在可以这样访问
>>> f = Fees()
>>> f.set_fee("1")
>>> f.fee
Decimal('1')
>>> f.fee = "2"
>>> f.get_fee()
Decimal('2')
正如你所看到的,当我们以这种方式使用属性函数时,它允许fee属性设置并获取值本身而不破坏原有代码。让我们使用属性装饰器来重写这段代码,看看我们是否能得到一个允许设置的属性值。
from decimal import Decimal
########################################################################
class Fees(object):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self._fee = None
#----------------------------------------------------------------------
@property
def fee(self):
"""
The fee property - the getter
"""
return self._fee
#----------------------------------------------------------------------
@fee.setter
def fee(self, value):
"""
The setter of the fee property
"""
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value
#----------------------------------------------------------------------
if __name__ == "__main__":
f = Fees()
上面的代码演示了如何为fee属性创建一个setter方法。你可以用一个名为@fee.setter的装饰器装饰第二个方法名也为fee的方法来实现这个。当你如下所做时,setter被调用:
>>> f = Fees()
>>> f.fee = "1"
如果你看属性函数的说明,它有fget, fset, fdel和doc几个参数。如果你想对属性使用del命令,你可以使用@fee.deleter创建另一个装饰器来装饰相同名字的函数从而实现删除的同样效果。
三、property函数
class C(object):
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。