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?
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):
...
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}
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
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')
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)