类方法替代初始化器
类方法提供了构建类实例的替代方法。为了说明,让我们看一个例子。
假设我们有一个相对简单的 Person
类:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
有一种方法可以构建此类的实例,分别指定全名而不是名字和姓氏。一种方法是将 last_name
作为可选参数,并假设如果没有给出,我们在下面传递全名:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split(" ", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
但是,这段代码有两个主要问题:
-
参数
first_name
和last_name
现在具有误导性,因为你可以输入first_name
的全名。此外,如果有更多的情况和/或更多参数具有这种灵活性,if / elif / else 分支可能会很快烦人。 -
不是那么重要,但仍值得指出:如果
last_name
是None
,但first_name
不会通过空格分成两个或更多的东西怎么办?我们还有另一层输入验证和/或异常处理……
输入类方法。我们将创建一个名为 from_full_name
的独立初始化程序,而不是使用单个初始化程序,并使用(内置)classmethod
装饰器进行装饰。
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split(" ", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
注意 cls
而不是 self
作为 from_full_name
的第一个参数。类方法应用于整个类,而不是给定类的实例(这是 self
通常表示的)。所以,如果 cls
是我们的 Person
类,那么 from_full_name
类方法的返回值是 Person(first_name, last_name, age)
,它使用 Person
的 __init__
来创建 Person
类的实例。特别是,如果我们要创建 Person
的子类 Employee
,那么 from_full_name
也可以在 Employee
类中工作。
为了表明这是按预期工作的,让我们以不止一种方式创建 Person
的实例,而不需要在 __init__
中进行分支:
In [2]: bob = Person("Bob", "Bobberson", 42)
In [3]: alice = Person.from_full_name("Alice Henderson", 31)
In [4]: bob.greet()
Hello, my name is Bob Bobberson.
In [5]: alice.greet()
Hello, my name is Alice Henderson.
其他参考: