Python Language Méthodes de classe: initialiseurs alternatifs


Exemple

Les méthodes de classe présentent des méthodes alternatives pour générer des instances de classes. Pour illustrer, regardons un exemple.

Supposons que nous ayons une classe de Person relativement simple:

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 + ".")

Il pourrait être utile d'avoir un moyen de construire des instances de cette classe en spécifiant un nom complet au lieu du nom et du prénom séparément. Une façon de le faire serait de faire en sorte que last_name soit un paramètre facultatif, et en supposant que s’il n’est pas donné, nous avons passé le nom complet dans:

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 + ".")

Cependant, il y a deux problèmes principaux avec ce bit de code:

  1. Les paramètres first_name et last_name sont maintenant trompeuses, puisque vous pouvez entrer un nom complet pour first_name . En outre, s'il y a plus de cas et / ou de paramètres ayant ce type de flexibilité, le branchement if / elif / else peut devenir rapidement ennuyeux.

  2. Pas tout à fait aussi important, mais ça vaut quand même la peine de le souligner: et si last_name est None , mais first_name ne se divise pas en deux ou plus via des espaces? Nous avons encore une autre couche de validation des entrées et / ou de gestion des exceptions ...

Entrez les méthodes de classe. Plutôt que d'avoir un seul initialiseur, nous allons créer un initialiseur distinct, appelé from_full_name , et le décorer avec le décorateur de classmethod (intégré).

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 + ".")

Remarquez cls au lieu de self comme premier argument de from_full_name . Les méthodes de classe sont appliquées à la classe globale, et non à une instance d'une classe donnée (ce que self désigne généralement). Donc, si cls est notre Person de classe, la valeur renvoyée par la from_full_name méthode de classe est Person(first_name, last_name, age) , qui utilise la Person de __init__ pour créer une instance de la Person classe. En particulier, si nous devions créer une sous-classe Employee of Person , alors from_full_name fonctionnerait également dans la classe Employee .

Pour montrer que cela fonctionne comme prévu, créons des instances de Person de plusieurs manières sans les branchements dans __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.

Autres références: