类方法替代初始化器

类方法提供了构建类实例的替代方法。为了说明,让我们看一个例子。

假设我们有一个相对简单的 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 + ".")

但是,这段代码有两个主要问题:

  1. 参数 first_namelast_name 现在具有误导性,因为你可以输入 first_name 的全名。此外,如果有更多的情况和/或更多参数具有这种灵活性,if / elif / else 分支可能会很快烦人。

  2. 不是那么重要,但仍值得指出:如果 last_nameNone,但 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.

其他参考: