当我可以在没有注释的情况下调用构造函数时,为什么要使用@classmethod?

why should i use @classmethod when i can call the constructor in the without the annotation?

我在研究@class方法的优点,发现我们可以直接从任何方法调用构造函数,在那种情况下,为什么我们需要一个class方法。有没有我遗漏的优点。

为什么是这个代码,有什么优点?

class Person: 
    def __init__(self, name, age): 
        self.name = name 
        self.age = age 

    @classmethod
    def fromBirthYear(cls, name, year): 
        return cls(name, date.today().year - year) 

而不是这个代码:-

class Person: 
    def __init__(self, name, age): 
        self.name = name 
        self.age = age 

    def fromBirthYear(name, year): 
        return Person(name, date.today().year - year) 

因为如果您从 Person 派生,fromBirthYear 将始终 return 一个 Person 对象而不是派生的 class。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fromBirthYear(name, year):
        return Person(name, year)


class Fred(Person):
    pass
print(Fred.fromBirthYear('bob', 2019))

输出:

<__main__.Person object at 0x6ffffcd7c88>

您可能希望 Fred.fromBirthYear 到 return 一个 Fred 对象。

最后语言会让你做很多不该做的事情

给定

from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fromBirthYear(name, year):
        return Person(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

只要您不通过 Person:

的实例访问 fromBirthYear,您的代码就可以找到
>>> Person("bob", 2010)
Person('bob', 10)

但是,从 Person 实例 调用它不会:

>>> Person("bob", 2010).fromBirthYear("bob again", 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fromBirthYear() takes 2 positional arguments but 3 were given

这是由于 function 类型如何实现描述符协议:通过实例访问调用其 __get__ 方法(return 是 method 对象"prepasses" 底层函数的实例),同时通过 class return 访问函数本身。


为了使事情更加一致,您可以将 fromBirthYear 定义为静态方法,无论是从 class 或实例:

from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @staticmethod
    def fromBirthYear(name, year):
        return Person(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"


>>> Person.fromBirthYear("bob", 2010)
Person('bob', 10)
>>> Person.fromBirthYear("bob", 2010).fromBirthYear("bob again", 2015)
Person('bob again', 5)

最后,class 方法的行为有点像静态方法,无论是从 class 调用还是从 class 的实例调用,接收到的参数都是一致的。但是,就像实例方法一样,它确实接收一个隐式参数:class 本身,而不是 class 的实例。这里的好处是可以在运行时确定由class方法编辑的实例return。假设您有 Person

的子class
from datetime import date
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def fromBirthYear(cls, name, year):
        return cls(name, date.today().year - year)

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

class DifferentPerson(Person):
    pass

两个class都可以用来调用fromBirthYear,但是return值现在取决于调用它的class。

>>> type(Person.fromBirthYear("bob", 2010))
<class '__main__.Person'>
>>> type(DifferentPerson.fromBirthYear("other bog", 2010))
<class '__main__.DifferentPerson'>