我一直在研究@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)
因为如果您从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对象。
最后,语言将使您可以执行很多不应该做的事情。
给出
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'>