乍一看之下,@staticmethod 与 @classmethod 真的很相似,貌似都可以看作是静态函数,除了后者必须有一个传入参数外就貌似没区别了。但既然加入了,那也一定有开发者的考虑了。
相同点:调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class TestClass():
@classmethod def class_foo(cls): print(cls) print('test in class_foo')
@staticmethod def static_foo(): print('test in static_foo')
if __name__ == '__main__': test = TestClass() test.class_foo() test.static_foo() TestClass.class_foo() TestClass.static_foo()
|
输出:
1 2 3 4 5 6
| <class '__main__.TestClass'> test in class_foo test in static_foo <class '__main__.TestClass'> test in class_foo test in static_foo
|
无论是@classmethod
还是@staticmethod
的调用者,即可以为一个已经是实例化的对象,也可以的是为一个类名,这一点上与 JAVA 或 C++ 的静态函数有点相似
主要区别:参数要求
@classmethod
比 @staicmethod
多要求一个参数,而多处的这个参数 cls
指代的就是调用者的类。从上一个例子可以看出,cls
实际上指代的就是调用者的类型,即test
的类型为TestClass
。
应用举例
构造多个 constructor(构造函数)
对于 C++ 以及 JAVA 函数来说,由于存在重载机制,因此可以多个构造函数。比如在 JAVA 的 Test 类中,我可以声明一个用数组初始化的构造函数,也可以声明一个由字符串初始化的构造函数。但由于 Python 没有这种机制,貌似有点棘手。但可以用 @classmethod 弥补。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class TestClass(): def __init__(self, lst): self.lst = lst
@classmethod def from_str(cls, s): lst = s.split() return cls(lst)
if __name__ == '__main__': t = TestClass([1, 2, 3]) print(t.lst) t = TestClass.from_str("1 2 3") print(t.lst)
|
输出:
1 2
| [1, 2, 3] ['1', '2', '3']
|
而如果用 @staticmethod 则显得无力了
构造工厂类
借助上面的特性,可以把在实际编写中写出一些简单的工厂类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Diagram(): @classmethod def make_circle(Class, color): return Class.Circle(color)
@classmethod def make_square(Class, color): return Class.Circle(color)
class Circle(): def __init__(self, color): self.color = color
class Square(): def __init__(self, color): self.color = color
class AnotherDiagram(): @classmethod def make_circle(Class, color): return Class.Circle(color)
@classmethod def make_square(Class, color): return Class.Circle(color)
class Circle(): def __init__(self, color): self.color = color
class Square(): def __init__(self, color): self.color = color
if __name__ == "__main__": factory = Diagram d_circle = factory.make_circle("red") d_square = factory.make_square("green")
factory = AnotherDiagram ad_circle = factory.make_circle("violet") ad_square = factory.make_square("pink") print(d_circle.color, d_square.color, ad_circle.color, ad_square.color)
|
例子中有两个工厂类:Diagram 和 AnotherDiagram 代表两种图表,两种图表都有 Circle 和Square 。另外值得注意的是,因为命名空间的原因,可以分别在两个工厂内声明 Circle 类和 Square 类。而不用大费力气的声明成这样:
1 2 3 4 5 6 7 8 9 10 11
| class DiagramCirle(): pass
class DiagramSquare(): pass
class AnotherDiagramCircle(): pass
class AnotherDiagramSquare(): pass
|
运用 @classmethod 的工厂方式可读性显得更佳。而且在 factory 调用函数,不需要知道自己是 Diagram 或 AnotherDiagram,直接调用需要的绘图方法即可。
参考
- Python编程实战 运用设计模式、并发和程序库创建高质量程序 / Python in Practice: Create Better Programs Using Concurrency, Libraries, and Patterns
- Stack Overflow - Python @classmethod and @staticmethod for beginner?
- 知乎 - Python 中的 classmethod 和 staticmethod 有什么具体用途?