Python 教程没有提到 classmethod 装饰器,也没有提到 staticmethod。学过 Java 面向对象编程的人可能觉得奇怪,为什么 Python 提供两个这样的装饰器,而不是只提供一个?

先来看 classmethod。示例 9-3 展示了它的用法:定义操作类,而不是操作实例的方法。classmethod 改变了调用方法的方式,因此类方法的第一个参数是类本身,而不是实例。classmethod 最常见的用途是定义备选构造方法,例如示例 9-3 中的 frombytes。注意,frombytes 的最后一行使用 cls 参数构建了一个新实例,即 cls(*memv)。按照约定,类方法的第一个参数名为 cls(但是 Python 不介意具体怎么命名)。

staticmethod 装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。示例 9-4 对 classmethodstaticmethod 的行为做了对比。

示例 9-4 比较 classmethodstaticmethod 的行为

>>> class Demo:
...     @classmethod
...     def klassmeth(*args):
...         return args  # ➊
...     @staticmethod
...     def statmeth(*args):
...         return args  # ➋
...
>>> Demo.klassmeth()  # ➌
(<class '__main__.Demo'>,)
>>> Demo.klassmeth('spam')
(<class '__main__.Demo'>, 'spam')
>>> Demo.statmeth()   # ➍
()
>>> Demo.statmeth('spam')
('spam',)

klassmeth 返回全部位置参数。

statmeth 也是。

❸ 不管怎样调用 Demo.klassmeth,它的第一个参数始终是 Demo 类。

Demo.statmeth 的行为与普通的函数相似。

 classmethod 装饰器非常有用,但是我从未见过不得不用 staticmethod 的情况。如果想定义不需要与类交互的函数,那么在模块中定义就好了。有时,函数虽然从不处理类,但是函数的功能与类紧密相关,因此想把它放在近处。即便如此,在同一模块中的类前面或后面定义函数也就行了。5

5本书的技术审校之一 Leonardo Rochael 不同意我对 staticmethod 的见解,作为反驳,他推荐阅读 Julien Danjou 写的一篇博客文章,题为“The Definitive Guide on How to Use Static, Class or Abstract Methods in Python”(https://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods)。Danjou 的这篇文章写得很好,我推荐阅读。但是,我对 staticmethod 的观点依然不变。请读者自辨。

现在,我们对 classmethod 的作用已经有所了解(而且知道 staticmethod 不是特别有用),下面继续讨论对象的表示形式,说明如何支持格式化输出。