Основная причина использования @classmethod
заключается в альтернативном конструкторе, который предназначен для наследования.Это может быть очень полезно при полиморфизме.Пример:
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()
Обратите внимание, что Shape
является абстрактным классом, который определяет метод from_square
, так как Shape
на самом деле не определен, он на самом деле не знает, как получить себя из Square
, поэтому он просто возвращает экземпляр класса по умолчанию.
Затем унаследованным классам разрешается определять свои собственные версии этого метода:
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)
Использование позволяет обрабатывать всеэти непроизведенные классы полиморфно
square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle):
this_shape = polymorphic_class.from_square(square)
Вы можете сказать, что это прекрасно, но почему я не могу просто использовать как @staticmethod
для достижения того же полиморфного поведения:
class Circle(Shape):
def __init__(self, radius=10):
self.radius = radius
@staticmethod
def from_square(square):
return Circle(radius=square.length/2)
Ответ в том, что вы могли бы, но вы не получаете преимуществ наследования, потому что Circle
должен быть явно вызван в методе.То есть, если я вызову его из унаследованного класса без переопределения, я все равно получу Circle
каждый раз.
Обратите внимание, что получается, когда я определяю другой класс фигур, который на самом деле не имеет никакой собственной логики from_square:
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
Здесь вы можете оставить @classmethod
неопределенным, и он будет использовать логикуот Shape.from_square
, сохраняя cls
и возвращая соответствующую форму.
square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle, Hexagon):
this_shape = polymorphic_class.from_square(square)