--- title: Instance Class Static Mathods fid: 20240113-201800 tags: --- (Realpython-Instance-Class-Static-Mathods)= # Instance Class Static Mathods 2024-01-13 From: [Python's Instance, Class, and Static Methods Demystified – Real Python](https://realpython.com/instance-class-and-static-methods-demystified/#instance-methods) ## 概念 ```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 ```python >>> obj = MyClass() >>> obj.method() ('instance method called', ) ``` 还可以这样用? ```python >>> MyClass.method(obj) ('instance method called', ) ``` 先要有 instance,然后才能有 instance method ### Class the Class Method ```python >>> obj.classmethod() ('class method called', ) ``` 能访问到的 cls 是 class MyClass, 而不是在 MyClass instance 请注意,将这些参数命名为 `self` 和 `cls` 只是一种惯例。你也可以很容易地将它们命名为 the_object 和 the_class,并得到同样的结果。重要的是,它们在方法的参数列表中位于第一位。 ### Call the Static Method static method 属于 instance 的名字空间。 在后台,Python 只是通过在使用点语法调用静态方法时不传递 `self` 或 `cls` 参数来执行访问限制。 这证明静态方法既不能访问对象实例状态,也不能访问类状态。 ```python >>> obj.staticmethod() 'static method called' ``` ### 实例化前调用 如果不实例化,类方法与静态方法都可以正确调用,但实例方法 (instance methods) 没有办法使用: ```python >>> MyClass.classmethod() ('class method called', ) >>> 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 定义对象工厂 ```python 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)原则的技巧。如果我们决定在某个时候重新命名这个类,我们就不必记得更新所有类方法工厂函数中的构造函数名称了。 ```python >>> Pizza.margherita() Pizza(['mozzarella', 'tomatoes']) >>> Pizza.prosciutto() Pizza(['mozzarella', 'tomatoes', 'ham']) ``` 想一想,现在的创建以上两个实例的过程。它们在内部都使用相同的 `__init__` 构造函数,只是提供了一个记住各种成分的快捷方式。 Python 只允许每个类使用一个 `__init__` 方法。使用类方法可以根据需要添加尽可能多的替代构造函数。这可以使类的接口自文档化(在一定程度上),并简化它们的使用。 ## When To Use Static Methods 当某个方法不需要访问、修改类和实例的任何属性时,就可以用 Static Method。 ```python 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()` 方法完全独立于类的其他部分,因此测试起来要容易得多。