乍一看之下,@staticmethod 与 @classmethod 真的很相似,貌似都可以看作是静态函数,除了后者必须有一个传入参数外就貌似没区别了。但既然加入了,那也一定有开发者的考虑了。
相同点:调用者
| 12
 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()
 
 | 
输出:
| 12
 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 弥补。
| 12
 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)
 
 | 
输出:
| 12
 
 | [1, 2, 3]['1', '2', '3']
 
 | 
而如果用 @staticmethod 则显得无力了
构造工厂类
借助上面的特性,可以把在实际编写中写出一些简单的工厂类
| 12
 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 类。而不用大费力气的声明成这样:
| 12
 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 有什么具体用途?