【问题标题】:Dynamic inheritance in PythonPython中的动态继承
【发布时间】:2014-01-30 08:33:00
【问题描述】:

假设我有 2 个不同的类实现

class ParentA:
    def initialize(self):
        pass

    def some_event(self):
        pass

    def order(self, value):
        # handle order in some way for Parent A


class ParentB:
    def initialize(self):
        pass

    def some_event(self):
        pass

    def order(self, value):
        # handle order in another for Parent B

我怎样才能动态地让一些第三类继承自 ParentAParentB 基于这样的东西?

class MyCode:
    def initialize(self):
        self.initial_value = 1

    def some_event(self):
        # handle event
        order(self.initial_value)


# let MyCode inherit from ParentA and run
run(my_code, ParentA)

【问题讨论】:

  • 我重新打开了这个问题,因为 dup 是关于在创建后更改实例的类,而这个问题是关于创建具有不同基类的

标签: python inheritance


【解决方案1】:

只需将类对象存储在一个变量中(在下面的示例中,它被命名为base),并在class 语句的基类规范中使用该变量。

def get_my_code(base):

    class MyCode(base):
        def initialize(self):
          ...

    return MyCode

my_code = get_my_code(ParentA)

【讨论】:

  • 智能简单。谢谢
  • 根据您使用相同参数调用get_my_code 的频率,这将创建许多基本相同但不同的类。某种形式的记忆会很好。
  • FYI.... 如果有人要求有多个父类(例如 ParentA、ParentB 在上述情况下),那么您可以使用 base 作为父类列表,然后执行class MyCode(*base):...... 实例化时,使用:my_code = get_my_code([ParentA, ParentB])
【解决方案2】:

另外,您可以使用 type 内置。作为可调用对象,它接受参数:name, bases, dct(最简单的形式)。

def initialize(self):
    self.initial_value = 1

def some_event(self):
    # handle event
    order(self.initial_value)

subclass_body_dict = {
    "initialize": initialize,
    "some_event": some_event
}

base_class = ParentA # or ParentB, as you wish

MyCode = type("MyCode", (base_class, ), subclass_body_dict)

这比 snx2 解决方案更明确,但仍然 - 我更喜欢他的方式。

PS。当然,您不必存储 base_class,也不必存储 subclass_body_dict,您可以在 type() 调用中构建这些值,例如:

MyCode = type("MyCode", (ParentA, ), {
        "initialize": initialize,
        "some_event": some_event
    })

【讨论】:

  • Filip,当我使用type() 时,父级将只有第三个参数中给出的方法。有什么方法可以保留其方法并仅覆盖第三个参数中给出的方法?
  • 好吧,我想通了。只需将子类添加到元组中即可。
  • 我陷入了一种情况,显然我的所有基础都是旧样式类。 (“一个新式的类不能只有经典的基础”)。或者我对元类太深入了。天哪!
  • 这听起来像是针对不同问题的材料。你能问一个关于SO的问题吗,请在此处链接,我会尽力回答;)
【解决方案3】:

就像一个快速复制和粘贴准备好的 sn-p,我添加了来自 shx2's answer 的 cmets 来创建它(使用 created_classes dict 属性进行记忆,以便通过连续相同的调用创建的类具有相同的类将给出相同的类):

class ParentA:
    val = "ParentA"

class ParentB:
    val = "ParentB"

class DynamicClassCreator():
    def __init__(self):
        self.created_classes = {}
    def __call__(self, *bases):
        rep = ",".join([i.__name__ for i in bases])
        if rep in self.created_classes:
            return self.created_classes[rep]
        class MyCode(*bases):
            pass
        self.created_classes[rep] = MyCode
        return MyCode

creator = DynamicClassCreator()

instance1 = creator(ParentA, ParentB)()
print(instance1.val) #prints "ParentA"

instance2 = creator(ParentB, ParentA)()
print(instance2.val) #prints "ParentB"

如果您想变得花哨,甚至可以将 DynamicClassCreator 设为 Singleton:https://stackoverflow.com/a/7346105/5122790

【讨论】:

    【解决方案4】:

    作为Chris's answershx2's answer 实现记忆建议的替代方案,我更喜欢使用memoize decorator(最终结果仍然是一个类,但对我来说更清楚的是函数是接口),并且还使用setdefault 来简化添加到备忘录字典中,并且不要将名称转换为字符串,而是使用元组bases 本身作为键,将代码简化为:

    class Memoize:
        def __init__(self, f):
            self.f = f
            self.memo = {}
    
        def __call__(self, *args):
            return self.memo.setdefault(args, self.f(*args))
    
    class ParentA:
        def initialize(self):
            pass
    
    
    class ParentB:
        def initialize(self):
            pass
    
    
    @Memoize
    def get_my_code(base):
    
        class MyCode(base):
            def initialize(self):
              pass
    
        return MyCode
    
    a1 = get_my_code(ParentA)
    a2 = get_my_code(ParentA)
    b1 = get_my_code(ParentB)
    
    print(a1 is a2) # True
    print(a1 is b1) # False
    

    (不是一个很好的例子,因为提供的代码实际上除了覆盖父类的 initialize 方法之外没有做任何事情......)

    【讨论】:

    • StackOverflow 很棒。再次偶然发现这个问题只是为了找到我自己回答的 Stackoverflow 问题,然后发现有人甚至改进了我的答案!太棒了!
    猜你喜欢
    • 2018-03-10
    • 1970-01-01
    • 2015-12-24
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-05-28
    相关资源
    最近更新 更多