web-dev-qa-db-fra.com

Quel est un exemple d'utilisation pour une méthode de classe Python?

J'ai lu Quelles sont les méthodes de classe dans Python pour? mais les exemples dans ce post sont complexes. Je cherche un exemple clair, simple et simple d'un cas d'utilisation particulier pour les méthodes de classe en Python.

Pouvez-vous nommer un petit exemple d'utilisation spécifique où une méthode de classe Python serait le bon outil pour le travail?

58
coffee-grinder

Méthodes d'aide à l'initialisation:

class MyStream(object):

    @classmethod
    def from_file(cls, filepath, ignore_comments=False):    
        with open(filepath, 'r') as fileobj:
            for obj in cls(fileobj, ignore_comments):
                yield obj

    @classmethod
    def from_socket(cls, socket, ignore_comments=False):
        raise NotImplemented # Placeholder until implemented

    def __init__(self, iterable, ignore_comments=False):
       ...
46
anijhaw

Eh bien, __new__ Est une méthode de classe assez importante. C'est de là que viennent généralement les instances

donc dict() appelle dict.__new__ bien sûr, mais il existe parfois un autre moyen pratique de faire des dict qui est la méthode de classe dict.fromkeys()

par exemple.

>>> dict.fromkeys("12345")
{'1': None, '3': None, '2': None, '5': None, '4': None}
28
John La Rooy

Je ne sais pas, quelque chose comme les méthodes de constructeur nommé?

class UniqueIdentifier(object):

    value = 0

    def __init__(self, name):
        self.name = name

    @classmethod
    def produce(cls):
        instance = cls(cls.value)
        cls.value += 1
        return instance

class FunkyUniqueIdentifier(UniqueIdentifier):

    @classmethod
    def produce(cls):
        instance = super(FunkyUniqueIdentifier, cls).produce()
        instance.name = "Funky %s" % instance.name
        return instance

Usage:

>>> x = UniqueIdentifier.produce()
>>> y = FunkyUniqueIdentifier.produce()
>>> x.name
0
>>> y.name
Funky 1
19
julkiewicz

Je trouve que j'utilise le plus souvent @classmethod pour associer un morceau de code à une classe, pour éviter de créer une fonction globale, pour les cas où je n'ai pas besoin d'une instance de la classe pour utiliser le code.

Par exemple, je pourrais avoir une structure de données qui ne considère une clé valide que si elle est conforme à un modèle. Je peux vouloir l'utiliser à l'intérieur et à l'extérieur de la classe. Cependant, je ne veux pas créer encore une autre fonction globale:

def foo_key_is_valid(key):
    # code for determining validity here
    return valid

Je préfère de loin regrouper ce code avec la classe à laquelle il est associé:

class Foo(object):

    @classmethod
    def is_valid(cls, key):
        # code for determining validity here
        return valid

    def add_key(self, key, val):
        if not Foo.is_valid(key):
            raise ValueError()
        ..

# lets me reuse that method without an instance, and signals that
# the code is closely-associated with the Foo class
Foo.is_valid('my key')
10
samplebias

La principale raison d'utiliser un @classmethod est dans un constructeur alternatif destiné à être hérité. Cela peut être très utile dans le polymorphisme. Un exemple:

class Shape(object):
    # this is an abstract class that is primarily used for inheritance defaults
    # here is where you would define classmethods that can be overridden by inherited classes
    @classmethod
    def from_square(cls, square):
        # return a default instance of cls
        return cls()

Notez que Shape est une classe abstraite qui définit une méthode de classe from_square, puisque Shape n'est pas vraiment défini, il ne sait pas vraiment comment se dériver d'un Square donc il retourne simplement une instance par défaut de la classe.

Les classes héritées sont alors autorisées à définir leurs propres versions de cette méthode:

class Square(Shape):
    def __init__(self, side=10):
        self.side = side

    @classmethod
    def from_square(cls, square):
        return cls(side=square.side)


class Rectangle(Shape):
    def __init__(self, length=10, width=10):
        self.length = length
        self.width = width

    @classmethod
    def from_square(cls, square):
        return cls(length=square.side, width=square.side)


class RightTriangle(Shape):
    def __init(self, a=10, b=10):
        self.a = a
        self.b = b
        self.c = ((a*a) + (b*b))**(.5)

    @classmethod
    def from_square(cls, square):
        return cls(a=square.length, b=square.width)


class Circle(Shape):
    def __init__(self, radius=10):
        self.radius = radius

    @classmethod
    def from_square(cls, square):
        return cls(radius=square.length/2)

L'utilisation vous permet de traiter toutes ces classes non fondées de manière polymorphe

square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle):
    this_shape = polymorphic_class.from_square(square)

Tout cela est beau et dandy, pourriez-vous dire, mais pourquoi ne pourrais-je pas simplement l'utiliser comme @staticmethod pour accomplir ce même comportement polymorphe:

class Circle(Shape):
    def __init__(self, radius=10):
        self.radius = radius

    @staticmethod
    def from_square(square):
        return Circle(radius=square.length/2)

La réponse est que vous pouvez, mais vous n'obtenez pas les avantages de l'héritage car Circle doit être appelé explicitement dans la méthode. Ce qui signifie que si je l'appelle à partir d'une classe héritée sans redéfinir, j'obtiendrais toujours Circle à chaque fois.

Remarquez ce qui est gagné lorsque je définis une autre classe de forme qui n'a pas vraiment de logique from_square personnalisée:

class Hexagon(Shape):
    def __init__(self, side=10):
        self.side = side

    # note the absence of classmethod here, this will use from_square it inherits from shape

Ici, vous pouvez laisser le @classmethod non défini et il utilisera la logique de Shape.from_square tout en conservant qui cls est et retourne la forme appropriée.

square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle, Hexagon):
    this_shape = polymorphic_class.from_square(square)
10
krayzk