在__init__中设置对象的超类?

5

在实例化对象时,是否可以传入一个类作为对象的基类?

例如:

class Red(object):
    def x(self):
        print '#F00'

class Blue(object):
    def x(self):
        print '#00F'

class Circle(object):
    def __init__(self, parent):
        # here, we set Bar's parent to `parent`
        self.x()

class Square(object):
    def __init__(self, parent):
        # here, we set Bar's parent to `parent`
        self.x()
        self.sides = 4

red_circle = Circle(parent=Red)
blue_circle = Circle(parent=Blue)
blue_square = Square(parent=Blue)

这将产生类似于以下效果:

class Circle(Red):
    def __init__(self):
        self.x()

但又不影响其他Circle实例。


3
你确定这是你想要组织的方式吗?看起来圆形和正方形应该有一个“颜色”属性。至少从普通英语的角度来看,红色和蓝色不是圆形和正方形的父类。 - unutbu
2
我认为你在根本上滥用了“面向对象设计”。 - S.Lott
@S.Lott - 我想我试图过于聪明。依赖注入对于我的任务将是足够的。 - Phillip B Oldham
5个回答

7
也许你需要的是一个类工厂:
#!/usr/bin/env python
class Foo(object):
    def x(self):
        print('y')

def Bar(parent=Foo):
    class Adoptee(parent):
        def __init__(self):
            self.x()
    return Adoptee()
obj=Bar(parent=Foo)

4
我同意 @AntsAasma 的观点。你应该考虑使用依赖注入。至少在所给出的示例中(我相信这个示例被大大简化以说明你的问题),形状的颜色更好地通过has-a关系而不是is-a关系来表示。
你可以通过将所需的颜色对象传递给构造函数,存储对它的引用,并将函数调用委托给该对象来实现这一点。这极大地简化了实现,同时仍保留了所需的行为。在此处查看示例:
class Red(object):
    def x(self):
        print '#F00'

class Blue(object):
    def x(self):
        print '#00F'

class Shape(object):
    def __init__(self,color):
        self._color=color
    def x(self):
        return self._color.x()

class Circle(Shape):
    def __init__(self, color):
        Shape.__init__(self,color)
        self.x()

class Square(Shape):
    def __init__(self, color):
        Shape.__init__(self,color)
        self.x()
        self.sides = 4

red_circle = Circle(color=Red())
blue_circle = Circle(color=Blue())
blue_square = Square(color=Blue())

编辑:修正示例代码中构造函数参数的名称


在Square的def __init__中,也许应该将“parent”更改为“color”。 - unutbu
是的,我想从许多父对象中继承并潜在地覆盖方法,但 DI 可能是最佳选择。 - Phillip B Oldham
思考方式已经改变,目前使用super()和多重继承有更好的解决方案。请搜索“super is super”以了解更多信息。 - Jamie Marshall

1

听起来你正在尝试使用继承来做一些它不应该做的事情。如果你能解释一下为什么想要这样做,也许可以找到更符合习惯且更健壮的方法来实现你的目标。


根据您的建议更新了我的问题。 - Phillip B Oldham
2
你能详细说明一下吗?我猜你的目标不是制作打印其颜色的形状。其他类需要看到动态给定的“父类”提供的方法吗?“父类”需要访问子对象的属性/方法吗?根据当前信息,我建议你将颜色的实例存储为属性,并通过该属性委托任何工作。 - Ants Aasma

0
如果你真的需要它,那么你可以使用type构造函数,例如在工厂函数中(或者在__new__方法内部,但这可能是更安全的方法):
class Foo(object):
    def x(self):
        print 'y'

class Bar(object):
    def __init__(self):
        self.x()

def magic(cls, parent, *args, **kwargs):
    new = type(cls.__name__, (parent,), cls.__dict__.copy())
    return new(*args, **kwargs)

obj = magic(Bar, parent = Foo)

0

就像其他人所说的那样,这是一个相当奇怪的用法,但是如果你真的想要它,那肯定是可行的(除了你在注释中凭空提出的神秘的Bar;-)。例如:

class Circle(object):
    def __init__(self, parent):
        self.__class__ = type('Circle', (self.__class__, parent), {})
        self.x()

这使得每个Circle实例都有自己的类(全部命名为Circle,但都不同)-- 这部分实际上是这种习惯用法有时非常有用的关键原因(当您想要使用新式类的“每个实例定制特殊方法”时:由于特殊方法始终在类上查找,要对其进行每个实例的定制,您需要每个实例具有不同的类!-)。 如果您希望尽可能共享类,则可能需要一个小的备忘录工厂函数来帮助:

_memo = {}
def classFor(*bases):
  if bases in _memo: return _memo[bases]
  name = '_'.join(c.__name__ for c in bases)
  c = _memo[bases] = type(name, bases, {})
  return c

(这里我还使用了不同的方法来命名结果类,例如在您的示例中使用类名Circle_RedCircle_Blue而不仅仅是Circle)。然后:

class Circle(object):
    def __init__(self, parent):
        self.__class__ = classFor(Circle, parent)
        self.x()

所以这个技术非常稳定,但我仍然认为它不太适合你所举的使用案例。然而,在其他情况下可能会很有用,所以我还是展示一下。


Alex,我在尝试使用你的第二个Circle定义(使用classFor)时遇到了TypeError: __class__ must be set to new-style class, not 'NoneType' object错误。我不确定出了什么问题。你能否帮忙看一下? - unutbu
1
糟糕,classFor 函数只需要 return c - unutbu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接