Jamal的博客

Python-@classmethod和@staticmethod的区别

基础概念

这个问题的原文答案在:http://pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/
一个类中的方法最常用的是实例方法,即以self为第一个参数的方法,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ClassStaticMethodTest(object):
def print(self, data):
print('print:(%s, %s)' % (self, data))
@classmethod
def print_class(cls, data):
print('print_class:(%s, %s)' % (cls, data))
@staticmethod
def print_static(data):
print('print_static: (%s)' % data)
a = ClassStaticMethodTest()
a.print('normal')
a.print_class('class')
ClassStaticMethodTest.print_class('classMethod')
a.print_static('static')
ClassStaticMethodTest.print_static('staticMethod')

他的输出是:

1
2
3
4
5
print:(<__main__.ClassStaticMethodTest object at 0x1011b8ac8>, normal)
print_class:(<class '__main__.ClassStaticMethodTest'>, class)
print_class:(<class '__main__.ClassStaticMethodTest'>, classMethod)
print_static: (static)
print_static: (staticMethod)

如果直接调用

1
ClassStaticMethodTest.print('aaa')

那么会报错:

1
2
3
Traceback (most recent call last):
ClassStaticMethodTest.print('aaa')
TypeError: print() missing 1 required positional argument: 'data'

从上面的代码中,我们可以看出差别:

1. 普通方法

其实是实例方法,即通过把实例作为第一个参数的方式调用方法,普通的类方法需要通过self参数隐式传递当前类对象的实例,普通方法只能通过类对象的实例来调用,知乎上有个图解释的比较好:

1
2
3
1,2参数传递给方法.
3 self参数指向当前实例自身.
4 我们不需要传递实例自身给方法,Python解释器自己会做这些操作的.

2. @classmethod

需要通过cls传递当前类对象,可以通过类对象的实例和类对象类调用(但是需要注意不管是通过类对象还是类对象的实例,最终都是传递的类对象)。self和cls只是PEP8编程的一种规范,并不是强制要求的,self通常作为实例方法的第一个参数,cls通常作为类方法的第一个参数,即通常用self来传递当前类对象的实例,cls传递当前类对象。

3. @staticmethod

不需要传递类对象或类对象的实例,其实就是一个静态方法。

4. 继承和覆盖

1
2
3
4
5
6
7
8
9
10
11
class B(ClassStaticMethodTest):
def print(self, data):
print('print:(%s, %s)' % (self, data))
@classmethod
def print_class(cls, data):
print('print_class:(%s, %s)' % (cls, data))
@staticmethod
def print_static(data):
print('print_static: (%s)' % data)

输出:

1
2
3
print:(<__main__.B object at 0x1014575f8>, b1)
print_class:(<class '__main__.B'>, b2)
print_static: (b3)

与普通类实际上是一样的

为什么不直接使用普通函数

@staticmethod是把函数嵌入到类中的一种方式,函数就属于类,同时表明函数不需要访问这个类。通过子类的继承覆盖,能更好的组织代码