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

问题描述 投票:0回答:2

我一直在研究@classmethods的优点,并发现我们可以从任何方法直接调用构造函数,在这种情况下,为什么我们需要一个类方法。我错过了一些优势吗?

为什么使用此代码,有什么好处?

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) 
python class-method
2个回答
0
投票

因为如果您从Person派生,fromBirthYear将始终返回一个Person对象,而不是派生类。

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返回一个Fred对象。

最后,语言将使您可以执行很多不应该做的事情。


0
投票

给出

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})"

您的代码可以找到,只要您不通过fromBirthYear的实例访问Person

>>> 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__方法(该方法将将实例“优先”传递给基础函数的method对象返回),而通过该类返回函数本身。


为了使事情更加一致,您可以将fromBirthYear定义为静态方法,通过always可以访问基础函数,无论是从类还是从实例进行访问:

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)

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

的子类
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

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

>>> type(Person.fromBirthYear("bob", 2010))
<class '__main__.Person'>
>>> type(DifferentPerson.fromBirthYear("other bog", 2010))
<class '__main__.DifferentPerson'>
© www.soinside.com 2019 - 2024. All rights reserved.