在Python中,为什么要使用类方法而不是实例方法?

8

我一直在努力理解classmethods。我知道它们的工作原理,但我不明白为什么要使用它们或不使用它们。

例如:

我知道我可以像这样使用实例方法:

class MyClass():
    def __init__(self):
        self.name = 'Chris'
        self.age = 27

    def who_are_you(self):
        print('Hello {}, you are {} years old'.format(self.name, self.age))

c = MyClass()
c.who_are_you()

我也知道通过使用classmethod,我可以在不创建类的实例的情况下调用who_are_you()方法:

class MyClass():
    name = 'Chris'
    age = 27

    @classmethod
    def who_are_you(cls):
        print('Hello {}, you are {} years old'.format(cls.name, cls.age))

MyClass.who_are_you()

我不明白为什么你会选择其中一种方法而不是另一种


当您拥有实例变量时,可以使用实例方法。使用类方法时,无法具有不同年龄或名称的不同实例。 - OneCricketeer
1
请看:https://dev59.com/mWct5IYBdhLWcg3wUb1- - Henry Florence
2
通常情况下,您不会创建一个类来代表一个人。相反,您会创建一个名为 Person 的类,并创建其实例来表示一个人。 - GingerPlusPlus
2
可能是当我应该使用@classmethod和def method(self)?的重复问题。 - OneCricketeer
2
除了Ginger所说的,你需要将Chris和27传递到Person类的构造函数中,而不是将它们硬编码到类中。 - OneCricketeer
我已经盯着那两篇文章看了很久,但仍然无法弄清楚原因。Spkoder解释的方式对我来说很有道理。 - Mantis
4个回答

21
在您的第二个例子中,您将名称和年龄硬编码到类中。如果名称和年龄确实是类的属性,而不是类的特定实例,则使用类方法是有意义的。但是,如果您的类是类似于“人类”这样的东西,其中有许多具有不同姓名和年龄的实例,则不可能创建一个类方法来访问特定实例的唯一名称和年龄。在这种情况下,您将需要使用一个实例方法。
一般而言:
- 如果您想要作为整个类访问类的属性,而不是该类的特定实例的属性,则使用类方法。 - 如果您想要访问/修改与类的特定实例相关联的属性,则需要使用实例方法。

6
我已经查看了许多classmethod的示例,而你的解释是我第一个能理解的。谢谢。 - Mantis

5

类方法是在你没有、不需要或不能拥有实例时调用的方法。有时,当以这种方式使用时,一个类可以作为单例。但是,类方法最常见的用法可能是作为一个非标准构造函数。

例如,Python的dict类有一个名为dict.fromkeys(seq, [value])的非标准构造函数。显然,不可能涉及到任何实例 - 整个重点是创建一个实例。但它不是标准的__init__()构造函数,因为它采用了稍微不同格式的数据。

标准库中也有类似的方法:int.from_bytesbytes.fromhexbytearray.fromhex()float.fromhex()

如果你考虑Unix标准库,fdopen函数是一个类似的想法 - 它从描述符构造文件,而不是从字符串路径。Python的open()将接受文件句柄而不是路径,因此不需要单独的构造函数。但是,这个概念比你想象的更常见。


0

@classmethod 声明方法是静态的,因此您可以在不创建类的新实例的情况下使用它。另一方面,在第一个示例中,您必须在使用方法之前创建实例。 静态方法在MVC模式中的控制器等方面非常有用,而非静态方法则用于模型。 更多关于 @classmethod 和 @staticmethod 的信息请参见https://dev59.com/mWct5IYBdhLWcg3wUb1-#12179752


0
为了阐述SPKoderaghast的答案,让我们制作一个合理的版本来描述你的类,名为Person。我们将使__init__接受他们的姓名和年龄,并添加一个备用构造函数,让我们以另一种形式传递数据--比如一个名为deets的字典,其中可能包含各种其他字段。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def who_are_you(self):
        print('Hello {}, you are {} years old'.format(self.name, self.age))

    @classmethod
    def from_deets(cls, deets):
        name = deets['name']
        age = deets['age']
        return cls(name, age)

使用示例:

steve = Person('Stephen', 27)
steve.who_are_you()  # -> Hello Stephen, you are 27 years old

mels_deets = {'name': 'Melissa', 'age': 32, 'location': 'Ottawa'}
mel = Person.from_deets(mels_deets)
mel.who_are_you()  # Hello Melissa, you are 32 years old

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接