本文主要参照了python学习手册(第四版),关于静态方法和类方法这部分有讲解了10几页,条理非常清楚,这里就摘一下重点。特别地,python学习手册在描述这部分内容,比较了python2.6和python3.0。 但是我这里仅在python 2.7.12讨论。
我以前的理解就是:静态方法通过(@staticmethod)修饰,类方法通过(@classmethod)甚至都知道怎么用代码去表示。根据学习手册中提到原理,不是去讨论具体怎么使用,而是按照以下的问题一步一步过来。
- 什么是python的静态方法以及类方法?
- 为什么类中要使用这些特殊方法?
- 使用静态和类方法?
- 函数装饰器@staticmethod, @classmethod
什么是python类中的静态方法以及类方法?
静态方法:与类中的简单的无实例的函数类似的工作,在调用时不需要实例参数。
这边有两个网友的解释挺好的。
staticmethod means: when this method is called, we don’t pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can’t access the instance of that class (this is useful when your method does not use the instance).
类方法:传递一个类而不是一个实例,Python自动把类传入类方法第一个参数(最左侧)中,并且不管它是通过一个类或一个实例调用:
classmethod means: when this method is called, we pass the class as the first argument instead of the instance of that class (as we normally do with methods). This means you can use the class and its properties inside that method rather than a particular instance.
为什么要在类中使用这些特殊方法?
程序在处理与类而不是与实例相关的数据。比如说,这里有一个这样的小功能。记录由一个类创建的实例的数目。
1) 一个类之外的简单的函数编码(如果在没有类中的静态方法或者类方法,很容易想到的一个方式)
def printNumInstances():
print ('Number of instances created: ', Spam.numInstances)
class Spam:
numInstances = 0
def __init__(self):
Spam.numInstances = Spam.numInstances + 1
>>>a = Spam()
>>>b = Spam()
>>>c = Spam()
>>>printNumInstances()
('Number of instances created: ', 3)
因为类名称对于简单函数而言是可读取的全局变量,这样可以正常的工作。在静态方法之前,这一结构是通用的方式。但这种方法不太理想,有如下的几点不足:
a. 它给该文件的作用域添加了一个额外的名称,并且该名称只能用来处理单个的类。
b. 该函数与类的之间关联性很小,实际上它的定义可能在类之外数百行代码之外的位置。
c. 这样的简单函数不能通过继承定制,由此,它们位于累的命名空间之外,子类不能通过重新定义这样的一个函数来直接替代或者扩展它。
2)将printNumInstances()放入类中,并通过一个实例调用它
class Spam:
numInstances = 0
def __init__(self):
Spam.numInstances = Spam.numInstances + 1
def printNumInstances(self):
print ('Number of instances created: ', Spam.numInstances)
>>>a, b, c = Spam(), Spam(), Spam()
>>>a.printNumInstances()
('Number of instances created: ', 3)
>>>Spam.printNumInstances(a)
('Number of instances created: ', 3)
>>>Spam().printNumInstances()
('Number of instances created: ', 4)
遗憾的是,如果我们没有一个实例可用,并且产生一个实例会改变类的数据,因此,这样的方法是无法工作的。所以一个更好的方案是在类中把一个方法标记为不需要一个实例。下一下节使用静态和类方法?展示如何做到这一点。
3.使用静态和类方法?
如上两种方案都不行的情况下,在Python 2.2~2.7中,就出现了另外一个选择,可以编写和类相关的简单的函数。可以使用静态和类方法编写类,两者都不需要在启用时传入实例参数。要设计这个类的方法时,类需要调用两个内置函数staticmethod和classmethod。它们都把一个函数标记为特殊的,如果是静态方法的话不需要实例,如果是一个类方法的话需要一个类参数。
class Spam:
numInstances = 0
def __init__(self):
Spam.numInstances = Spam.numInstances + 1
def printNumInstances():
print ('Number of instances created: ', Spam.numInstances)
printNumInstances = staticmethod(printNumInstances)
>>>a, b, c = Spam(), Spam(), Spam()
>>>Spam.printNumInstances()
('Number of instances created: ', 3)
注意: 如果使用的是Python 3.0, 不调用内置函数staticmethod,能够得到正确结果,但在python 2.x中会提示TypeError: unbound method printNumInstances()。如上程序代码中最后的赋值语句只是重新赋值方法名称printNumInstances 而已。在Class语句中,通过赋值语句进行属性的建立和修改,这些最后的赋值语句会覆盖稍早有def所做的赋值。
这样做吧函数名称编程作用域内的局部变量。并把代码移到靠近其使用的地方,并且允许子类用继承定制静态方法。
class Sub(Spam):
def printNumInstances():
print('Extra Stuff...')
Spam.printNumInstances()
printNumInstances = staticmethod(printNumInstances)
>>>a, b = Sub(), Sub()
>>>a.printNumInstances()
Extra Stuff...
('Number of instances created: ', 2)
>>>Sub.printNumInstances()
Extra Stuff...
('Number of instances created: ', 2)
>>>Spam.printNumInstances()
('Number of instances created: ', 2)
此外,类可以继承静态方法而不用重新定义。
class Other(Spam): pass
>>>c = Other()
>>>c.printNumInstances()
('Number of instances created: ', 3)
类方法可以做类似的行为:
class Spam:
numInstances = 0
def __init__(self):
Spam.numInstances = Spam.numInstances + 1
def printNumInstances(cls):
print('Number of instances created: ', cls.numInstances)
printNumInstances = classmethod(printNumInstances)
>>>a, b, c = Spam(), Spam(), Spam()
>>>a.printNumInstances()
('Number of instances created: ', 3)
>>>Spam.printNumInstances()
('Number of instances created: ', 3)
4.函数装饰器@staticmethod, @classmethod
函数装饰器提供一种方式,替函数明确了特定的运算模式,也就是将函数包裹了另一层,在另一函数的逻辑内实现。从语法上来讲,函数装饰器是它后边函数运行时的声明。
如下:
class C:
@staticmethod
def method():
···
从内部来看,这个语法和下面的写法有着相同的效果。(把函数传递给装饰器,在赋值给最初的变量名)
class C:
def method():
···
method = staticmethod(method)
结果就是,调用方法函数的名称,实际上是触发了它staticmethod装饰器的结果。@classmethod类似原理。