Python 在_init中使用继承的类方法__

Python 在_init中使用继承的类方法__,python,inheritance,class-method,Python,Inheritance,Class Method,我有一个由几个孩子继承的父类。我想使用父级的@classmethod初始值设定项初始化其中一个子级。我该怎么做?我试过: class Point(object): def __init__(self,x,y): self.x = x self.y = y @classmethod def from_mag_angle(cls,mag,angle): x = mag*cos(angle) y = mag*si

我有一个由几个孩子继承的父类。我想使用父级的
@classmethod
初始值设定项初始化其中一个子级。我该怎么做?我试过:

class Point(object):
    def __init__(self,x,y):
        self.x = x
        self.y = y

    @classmethod
    def from_mag_angle(cls,mag,angle):
        x = mag*cos(angle)
        y = mag*sin(angle)
        return cls(x=x,y=y)


class PointOnUnitCircle(Point):
    def __init__(self,angle):
        Point.from_mag_angle(mag=1,angle=angle)


p1 = Point(1,2)
p2 = Point.from_mag_angle(2,pi/2)
p3 = PointOnUnitCircle(pi/4)
p3.x #fail

如果您尝试这样编写
\uuuu init\uuuu
,您的
PointOnUnitCircle
Point
具有不同的接口(因为它需要
角度,而不是
x,y
),因此不应该是它的子类。比如说:

class PointOnUnitCircle(Point):

    def __init__(self, x, y):
        if not self._on_unit_circle(x, y):
            raise ValueError('({}, {}) not on unit circle'.format(x, y))
        super(PointOnUnitCircle, self).__init__(x, y)

    @staticmethod
    def _on_unit_circle(x, y):
        """Whether the point x, y lies on the unit circle."""
        raise NotImplementedError

    @classmethod
    def from_angle(cls, angle):
        return cls.from_mag_angle(1, angle)

    @classmethod
    def from_mag_angle(cls, mag, angle):  
        # note that switching these parameters would allow a default mag=1
        if mag != 1:
            raise ValueError('magnitude must be 1 for unit circle')
        return super(PointOnUnitCircle, cls).from_mag_angle(1, angle)
这使接口保持不变,添加了检查子类输入的逻辑(一旦编写了它!),并提供了一个新的类方法,可以从
角度轻松构建新的
PointOnUnitCircle
。而不是

p3 = PointOnUnitCircle(pi/4)
你必须写作

p3 = PointOnUnitCircle.from_angle(pi/4)

您可以重写子类的
\uuuu new\uuu
方法,以从超类的备用构造函数构造实例,如下所示

import math


class Point(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def from_polar(cls, radius, angle):
        x = radius * math.cos(angle)
        y = radius * math.sin(angle)
        return cls(x, y)


class PointOnUnitCircle(Point):

    def __new__(cls, angle):
        point = Point.from_polar(1, angle)
        point.__class__ = cls
        return point

    def __init__(self, angle):
        pass
请注意,在
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
中,行
point=point.from\uPolar(1,angle)
不能替换为
point=super()。from\uPolar(1,angle)
是因为
point
作为备用构造函数的第一个
将子类
PointOnUnitCircle
发送到备用构造函数,该构造函数循环调用调用它的子类的
\uuuuuuu new\uuuuuu
,依此类推,直到发生
递归错误。还要注意的是,即使子类中的
\uuuuuu init\uuuu
为空,但在子类中不重写
\uuuuuuu init\uuuu
的情况下,超类的
\uuuu init\uuuuu
会在
\uuu new\uuuu
之后立即自动调用,从而撤消备用构造函数

或者,某些对象设计使用组合比使用继承更简单。例如,您可以替换上面的
PointOnUnitCircle
类,而不使用以下类重写
\uuuu new\uuuu

class UnitCircle:

    def __init__(self, angle):
        self.set_point(angle)

    def set_point(self, angle):
        self.point = Point.from_polar(1, angle)

@classmethod
self
?!类方法的第一个参数通常命名为
cls
,以区别于实例
self
。您当然可以通过
self
访问类方法,但在本例中,由于类方法设置了一个class属性,因此不清楚您为什么要访问类方法。您不能在
\uuu init\uuu
中分配给
self
,并期望它能正常工作。如果类方法是替代构造函数,为什么不直接使用
A10.from_half_a(5)
?类方法调用
\uuuuuu init\uuuu
,而不是相反。继承已经实现了您想要的功能,因为您在继承的方法中使用了
cls(…)
而不是
A(…)
。@alex这不是意见问题!当您从类方法内部调用
cls(…)
时,该类方法对
cls
表示的任何类调用
\uuuuu init\uuuuu
(和/或
\uuuuu new\uuuuuuuu
),创建一个新实例。“我试图做的只是从子类使用父类的替代构造函数”,因此,再次使用
ChildClass.inherited\u class\u方法(…)
。例如,请参阅,了解为什么不能分配给
self
。这正是我的答案所讨论的问题。您的“子类”现在与父类具有完全不同的签名,如果您从_polar
直接调用它,则会发生不好的事情。不能只在使用父对象的地方使用它。这个实现是多么笨拙,这暗示了它是一个坏主意。我必须把
从_polar
变成一个静态方法来解决这个问题,这可能会导致更多的问题。放弃与
super
的兼容性,进入
\uuuuuuuuuu新的
\uuuuuuuuuu类
似乎是危险信号。我很高兴你指出了缺陷。如果子类只对超类的替代构造函数有意义,那么最好使用组合,因为即使使用您提供的无褶皱解决方案,子类的正常构造也不再有用,从这个意义上讲,正如您所说,“您不能在使用父类的任何地方使用它。”