Instance Class Static Mathods¶
2024-01-13
From: Python's Instance, Class, and Static Methods Demystified – Real Python
概念¶
class MyClass:
def method(self): #/// regular instance method
return 'instance method called', self
@classmethod
def classmethod(cls):
return 'class method called', cls
@staticmethod
def staticmethod():
return 'static method called'
instance method¶
regular instance method 一定具有 self 参数,指同 instance of MyClass
通过 self 参数,实例方法可以自由访问同一对象上的属性和其他方法。这使得它们在修改对象状态时拥有很大的权力。
实例方法不仅可以修改对象状态,还可以通过 self.__class__ 属性访问类本身。这意味着实例方法也可以修改类的状态。
Class Methods¶
@classmethod 装饰器被用来标记Class Methods (类方法)。它不接受参数 self,但却接受参数 cls。
由于类方法只能访问 cls 参数,因此无法修改对象实例状态。这需要访问 self。不过,类方法仍然可以修改适用于类的所有实例的类状态。
Static Methods¶
@classmethod 装饰器被用来标记 Static Methods (静态方法)。
这类方法既不接受 self 参数,也不接受 cls 参数(当然也可以接受任意数量的其他参数)。
因此,静态方法既不能修改对象状态,也不能修改类状态。静态方法在访问数据方面受到限制,而且静态方法主要是给方法提供名字空间。
Call the Method¶
Call the instance method¶
>>> obj = MyClass()
>>> obj.method()
('instance method called', <MyClass instance at 0x10205d190>)
还可以这样用?
>>> MyClass.method(obj)
('instance method called', <MyClass instance at 0x10205d190>)
先要有 instance,然后才能有 instance method
Class the Class Method¶
>>> obj.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)
能访问到的 cls 是 class MyClass, 而不是在 MyClass instance
请注意,将这些参数命名为 self 和 cls 只是一种惯例。你也可以很容易地将它们命名为 the_object 和 the_class,并得到同样的结果。重要的是,它们在方法的参数列表中位于第一位。
Call the Static Method¶
static method 属于 instance 的名字空间。
在后台,Python 只是通过在使用点语法调用静态方法时不传递 self 或 cls 参数来执行访问限制。
这证明静态方法既不能访问对象实例状态,也不能访问类状态。
>>> obj.staticmethod()
'static method called'
实例化前调用¶
如果不实例化,类方法与静态方法都可以正确调用,但实例方法 (instance methods) 没有办法使用:
>>> MyClass.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)
>>> MyClass.staticmethod()
'static method called'
>>> MyClass.method()
TypeError: unbound method method() must
be called with MyClass instance as first
argument (got nothing instead)
@classmethod 对象工厂¶
利用 classmethod 定义对象工厂
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'Pizza({self.ingredients!r})'
@classmethod
def margherita(cls):
return cls(['mozzarella', 'tomatoes'])
@classmethod
def prosciutto(cls):
return cls(['mozzarella', 'tomatoes', 'ham'])
在 classmethod 中,使用了 cls 代替 Pizza Class, 这是一个遵循 "不要重复自己"(DRY)原则的技巧。如果我们决定在某个时候重新命名这个类,我们就不必记得更新所有类方法工厂函数中的构造函数名称了。
>>> Pizza.margherita()
Pizza(['mozzarella', 'tomatoes'])
>>> Pizza.prosciutto()
Pizza(['mozzarella', 'tomatoes', 'ham'])
想一想,现在的创建以上两个实例的过程。它们在内部都使用相同的 __init__ 构造函数,只是提供了一个记住各种成分的快捷方式。
Python 只允许每个类使用一个 __init__ 方法。使用类方法可以根据需要添加尽可能多的替代构造函数。这可以使类的接口自文档化(在一定程度上),并简化它们的使用。
When To Use Static Methods¶
当某个方法不需要访问、修改类和实例的任何属性时,就可以用 Static Method。
import math
class Pizza:
def __init__(self, radius, ingredients):
self.radius = radius
self.ingredients = ingredients
def __repr__(self):
return (f'Pizza({self.radius!r}, '
f'{self.ingredients!r})')
def area(self):
return self.circle_area(self.radius)
@staticmethod
def circle_area(r):
return r ** 2 * math.pi
在上例中,circle_area() 显然不能以任何方式修改类或类实例。它也不能访问类或实例的属性。它就是一个在类或实例中使用的普通方法。
通过这样的技术,您可以清晰地沟通类架构的各个部分,从而自然而然地引导新的开发工作在这些设定的边界内进行。在实践中,它们往往有助于避免违反原设计的意外修改。
静态方法在编写测试代码时也有好处。由于 circle_area() 方法完全独立于类的其他部分,因此测试起来要容易得多。