【问题标题】:How to improve a mixin structure in Python?如何改进 Python 中的 mixin 结构?
【发布时间】:2016-09-28 13:35:28
【问题描述】:

我在 Python 中有一个简单的 mixin 结构。代码应该很不言自明:

class Base:
    def __init__(self):
        pass

class MixinA:
    def __init__(self):
        self.x = 0
        self.y = 1
    def a(self):
        print('A: x = ' + str(self.x) + ', y = ' + str(self.y))

class MixinB:
    def __init__(self):
        self.x = 2
        self.z = 3
    def b(self):
        print('B: x = ' + str(self.x) + ', z = ' + str(self.z))

class MyFirstMix(MixinA, MixinB, Base):
    def __init__(self):
        Base.__init__(self)
        MixinB.__init__(self)
        MixinA.__init__(self)

class MySecondMix(MixinA, Base):
    def __init__(self):
        Base.__init__(self)
        MixinA.__init__(self)

我想稍微改进一下,所以这会导致 3 个问题/问题:

  1. MixinAMixinB 都有一个成员 x。有没有办法确保每个班级只看到自己的 x? 据我所知:不,没有。
  2. 为混合类中的每个mixin调用构造函数有点麻烦。有没有办法自动调用所有的构造函数或做一些具有相同效果的事情?
  3. 有没有一种方法可以在不显式创建类的情况下动态地在线混合某些内容?我正在寻找类似的语法:mix = Base() with MixinA

如果我提议的结构完全错误,我也愿意接受有关如何处理 mixins 的其他建议。

【问题讨论】:

  • Prevent function overriding in Python 的可能重复项。您可以使用此处提到的类似技术来防止实例属性被覆盖。
  • @NoctisSkytower:那么,问题 2 和问题 3 是怎么回事?
  • 这些可以并且可能应该作为单独的问题发布。

标签: python mixins


【解决方案1】:

对于python类固有的,我相信有一些技巧你需要知道:

  1. python2 和 python3 中的类完全不同。 Python2 支持 old-style class,但 python3 只支持 new-style class。简单地说:在 python3 中,类总是来自基类object,即使您没有明确使用它。检查Difference-between-Old-New-Class

  2. 方法解析顺序 (MRO)。这决定了派生类如何搜索固有成员和函数。见MRO

  3. super 函数。结合MRO,可以方便地调用父成员或函数,而无需明确知道父类的名称。见Super

现在向您提出问题:

  1. MixinA 和 MixinB 都有一个成员 x。有没有办法确保每个班级只看到自己的 x?

我不明白你的意思。当你引用一个类成员时,你必须通过它的实例或类来引用它。所以instance_of_MixinA.xinstance_of_MixinB.x 是分开的。如果您在谈论MyFirstMix(MixinA, MixinB, Base) 类,则取决于如何调用__init__ 函数。在您的代码中,您首先用MixinB 填充x,然后用MixinA 重置其值。

  1. 为混合类中的每个mixin调用构造函数有点麻烦。有没有办法自动调用所有构造函数或做一些具有相同效果的事情。

您的指定使这成为不可能。你必须调用所有的构造函数。

  1. 有没有一种方法可以在不显式创建类的情况下动态混合内联内容?

我不确定。但是我可以给你一些建议:在def类时尝试在__init__之外的成员(python3,如果你使用python2照顾super):

class Base:
    def __init__(self):
        pass


class MixinA:
    x = 0
    y = 1


class MixinB:
    x = 2
    z = 3

    def b(self):
        print('B: x = ' + str(self.x) + ', z = ' + str(self.z))


class MyFirstMix(MixinA, MixinB, Base):
    def __init__(self):
        super().__init__()


class MySecondMix(MixinA, Base):
    def __init__(self):
        super().__init__()

__init__ 外部的变量与内部变量的行为完全不同:外部变量属于类,并且将为该类的所有实例填充,而内部变量仅属于实例(在定义类时由 self 引用) , 并且仅在调用 __init__ 时才会被填充。这就是为什么你不能使用super来调用所有的构造函数---super只能调用优先父级的__init__。见variables-outsite-inside-init

这是对 Mixin 类的一个很好的解决方案。在上面的代码中,MyFirstMix 固有的MixinAMixinB 的成员都是类成员(__init__ 之外)。因此MyFirstMix 的实例将继承MixinAMixinB 的所有类成员,而无需调用__init__。这里MixinAMixinB拥有同一个类成员x,但是MRO决定当MyFirstMix的实例引用x时,应该返回来自MixinAx

希望这会有所帮助。谢谢!

【讨论】:

    【解决方案2】:

    当您的继承方案开始遇到这些问题时,是时候考虑使用一种称为 composition 的技术了。对主题here 的可读性很好的介绍。 Wikipedia example 不太容易访问,但如果您可以处理其他编程语言,它也很有用。 This StackExchange question 也提供有用的讨论。

    在最简单的情况下,不是从SomeParent 继承的类并混合在Mixin 类中,而是让SomeParent 实例各自创建一个Mixin 实例并使用它来访问mixin 类的功能.

    【讨论】:

      猜你喜欢
      • 2021-05-02
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 2013-04-10
      • 2015-07-29
      • 2021-10-05
      • 1970-01-01
      • 2023-03-06
      相关资源
      最近更新 更多