类属性和实例属性
在开始整理这两个Decorator之前,先来理一理类和实例之间的关系,那就从属性开始吧,其实属性和方法,大家可以把他们看作是一样的,在python中,一切都是对象,在逻辑上,属性和方法,是一样的。
class Pcl(object):
class_data = 2
def __init__(self, data):
self.data = data
p1 = Pcl(10)
p2 = Pcl(20)
print('--------------------------------')
print('p1.data:', p1.data)
print('p2.data:', p2.data)
# print('Pcl.data: ', Pcl.data)
print('p1.class_data:', p1.class_data)
print('p2.class_data:', p2.class_data)
print('Pcl.class_data:', Pcl.class_data)
p1.class_data = 100
print('--------------------------------')
print('p1.class_data:', p1.class_data)
print('p2.class_data:', p2.class_data)
print('Pcl.class_data:', Pcl.class_data)
Pcl.class_data = 50
print('--------------------------------')
print('p1.class_data:', p1.class_data)
print('p2.class_data:', p2.class_data)
print('Pcl.class_data:', Pcl.class_data)
打印的结果会是
>>>--------------------------------
>>>p1.data: 10
>>>p2.data: 20
>>>p1.class_data: 2
>>>p2.class_data: 2
>>>Pcl.class_data: 2
>>>--------------------------------
>>>p1.class_data: 100
>>>p2.class_data: 2
>>>Pcl.class_data: 2
>>>--------------------------------
>>>p1.class_data: 100
>>>p2.class_data: 50
>>>Pcl.class_data: 50
- 创建了两个对象,他们的data属性是互不相干的,这个很好理解
- 类属性class_data对象之间共享同一个
- 当修改类属性的时候,发生了一些神奇的事情
p1.class_data = 100
这一步其实并没有修改那个类属性class_data,而是给p1这个对象创建了一个新的属性,恰好命名也是class_data,所以打印结果就如上所示了- 而当我们执行
Pcl.class_data = 50
这一句的时候,才真正修改了那个类属性,所以接下来的打印结果,p1因为有了和类属性相同命名的一个属性,所以还是100,p2和Pcl调用的结果,都变成了50
Instance Method
大多数时候,类中的方法都是这种实例方法,随意举个栗子:
class Pcl(object):
def __init__(self, data):
self.data = data
def foo(self):
print(self.data)
Pcl(10).foo()
打印的结果是
>>>10
简单解释一下,Instance Method的参数第一个必须是self,代表自身对象的引用,实例的任何属性和任何方法,都通过self来调用。
@classmethod
基于Instance Method,当一个方法中,没有通过self来做任何调用,但是调用了类属性(方法),Python建议把这个方法前加上@classmethod,再来举个栗子:
class Pcl(object):
class_data = 0
def __init__(self):
Pcl.class_data += 1
@classmethod
def bar(cls):
print(cls.class_data)
a = Pcl()
b = Pcl()
a.bar()
Pcl.bar()
可以看到,构造了两个对象,class_data
的值向上叠加了2,所以打印结果是:
>>>2
>>>2
对象和类都可以直接调用bar方法,这点和InstanceMethod不同。classmethod的第一个参数,也是Python规定的,必须是cls,代表类本身,cls.class_data
相当于Pcl.class_data
为什么说这种情况加上@classmethod是Python建议呢,Python的灵活之处就在这里,不加@classmethod,对程序运行毫无影响,至于调用类属性(方法)有其它办法,如下再来个栗子:
class Pcl(object):
class_data = 0
def __init__(self):
Pcl.class_data += 1
def bar(self):
print(self.__class__.class_data)
print(Pcl.class_data)
a = Pcl()
b = Pcl()
a.bar()
运行结果如下
>>>2
>>>2
看到这里相信大家都明白是怎么回事了。
@staticmethod
至于@staticmethod,一般情况下,如果该方法中,没有引用到这个类的任何其它方法和属性,保持相对独立,Python就建议将该方法设为staticmethod。栗子来了:
switch = True
class Pcl(object):
@staticmethod
def check_switch():
return switch
def foo(self):
if self.check_switch():
print('switch is True')
else:
print('switch is False')
Pcl().foo()
可以看到,staticmethod没有任何必须要加上的参数,打印结果自然如下:
>>>switch is True
总结
在我理解,@staticmethod和@classmethod的出现,完全是为了代码的可维护性的,没有他们不会使代码不可运行,但是可以使得代码结构更加科学。