【问题标题】:Selective Inheritance Python选择性继承 Python
【发布时间】:2012-09-19 09:23:23
【问题描述】:

我正在制作一个使用类的 python 程序,我希望一个类只能选择性地从另一个类继承,例如:

class X(object):
    def __init__(self):
        self.hello = 'hello'

class Y(object):
    def __init__(self):
        self.moo = 'moo'

class Z():
    def __init__(self, mode):
        if mode == 'Y':
             # Class will now Inherit from Y
        elif mode == 'X':
             # Class will now Inherit for X

如何在不上其他课程的情况下做到这一点?

【问题讨论】:

  • 您能否提供更多信息,说明您为什么要这样?考虑到这些赤裸裸的事实,作为一个设计决策,我觉得这有点奇怪。

标签: python


【解决方案1】:

在 Python 中可以在运行时创建类:

class X(object):
    def __init__(self):
        self.hello = 'hello'

class Y(object):
    def __init__(self):
        self.moo = 'moo'

def create_class_Z(mode):
    base_class = globals()[mode]
    class Z(base_class):
        def __init__(self):
            base_class.__init__(self)
    return Z

ZX = create_class_Z('X')
zx = ZX()
print(zx.hello)

ZY = create_class_Z('Y')
zy = ZY()
print(zy.moo)

【讨论】:

    【解决方案2】:

    您可以通过overriding __new__ 并更改传入的cls 来执行此操作(您通过附加XY 作为基类来创建新类型):

    class X(object):
        def __init__(self):
            self.hello = 'hello'
    
    class Y(object):
        def __init__(self):
            self.moo = 'moo'
    
    class Z(object):
        def __new__(cls, mode):
            mixin = {'X': X, 'Y': Y}[mode]
            cls = type(cls.__name__ + '+' + mixin.__name__, (cls, mixin), {})
            return super(Z, cls).__new__(cls)
        def __init__(self, mode, *args, **kwargs):
            super(Z, self).__init__(*args, **kwargs)
    

    注意,需要使用super绕过Z.__new__,以避免无限递归;这是__new__ 特殊覆盖方法的标准模式。

    【讨论】:

    • 我认为与在 Z 类之外的函数中创建新类型相比,这种解决方案过于复杂,避免了无限递归陷阱......
    • @l4mpi 这不是一个陷阱;这是标准的成语。编写__new__ 的优点是可以将相关代码保持在一起并保持与现有代码的兼容性。
    • 编写另一个函数的优点是能够重用它 - 我的解决方案被硬编码为使用类 Z 作为第一个基类,但你可以将它放在一个带有类和映射的闭包中基类并将其用于多个类。这样做意味着您要为每个要以这种方式使用的类添加大约两行代码 - 将其与其余代码保持在一起应该不会太难。另外,能否详细说明一下兼容性部分?
    • @l4mpi 如果您的代码在任何地方引用Z(例如,在Z 方法和super 中,在其他代码中访问静态方法或静态成员,在测试中使用monkeypatching)那么所有代码​​都需要Z 是真正的班级。
    • @l4mpi 也是,子类化 Z 会中断;我的解决方案非常适合这种用法。
    【解决方案3】:

    我认为你最好在Z中定义两个成员,一个是X的类实例,另一个是Y的实例。您可以在使用不同的模式时获取这些实例中存储的关联信息。

    【讨论】:

    • 他想要两个不同的类,而不是两个不同的实例。我看不出您提出的解决方案有什么用处。
    【解决方案4】:

    使用type的解决方案:

    class _Z(): pass #rename your class Z to this
    
    def Z(mode): #this function acts as the constructor for class Z
        classes = {'X': X, 'Y': Y, 'Foo': Bar} #map the mode argument to the base cls
        #create a new type with base classes Z and the class determined by mode
        cls = type('Z', (_Z, classes[mode]), {})
        #instantiate the class and return the instance
        return cls()
    

    【讨论】:

    • 这失败了isinstance(Z('X'), Z)
    • @ecatmur 是的,但无论如何你都不应该使用 isinstance:canonical.org/~kragen/isinstance
    • 那已经过时了; Python 现在有 ABC 和虚拟接口。
    • @ecatmur 与 EAFP 方法相比,我看不出这如何使对 isinstance 的批评无效......但无论如何,如果绝对需要,你可以使用isinstance(Z('X'), _Z)
    • PEP 3119 讨论了接口和虚拟子类在哪些方面优于 EAFP。我真正的意思是你违反了Z 是一个类的基本假设。
    猜你喜欢
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    相关资源
    最近更新 更多