【问题标题】:Can a Python class not inherit from multiple classes, but have a "choice" of inheritance?一个 Python 类可以不从多个类继承,而是有一个“选择”的继承吗?
【发布时间】:2017-03-15 21:22:30
【问题描述】:

我目前正在使用 3Rd 方应用程序。这个第 3 方应用程序定义了这些类:

 class VeryBaseClass(object):
      def __init__():
          pass

 class BaseClass(VeryBaseClass):
     def specific_method():
          pass

然后,论文的战利品:

 class Componenent[1,2,3,4....129883](BaseClass):
     def other_specific_method():
         pass

我无法修改这些类中的任何一个。所以,我在这里重写/补充方法,我只需要创建一个继承自 Component 的类,我可以毫不费力地更改方法。

问题是,制作 MyComponent1,然后是 MyComponent2,MyComponent3 等等……都是为了在类的内容中复制粘贴完全相同的代码,只是更改继承非常烦人,而且根本不是 DRY!

那么,有没有办法创建,例如,这个类:

class MyComponentGeneric(Component1, Component2, Component3....):
      pass

MyComponentGeneric 不会从列出的每个类继承,但可以从一个或另一个继承,这取决于我对实例的声明?

谢谢!

用更具体的代码编辑

实际上,我已经尝试过每个答案都属于的东西,但我总是遇到同样的错误:

我按照 Chepney 的建议做了一个工厂:

# This method is the one I use as a replacement
def newrender(self, name, value, attrs=None):
    result = super().render(name, value, attrs)
    if self.image is None:
        return result
    else:
        return '<img class="form-image" height="100" width="100" src="{}"/>'.format(self.image.url) + result


def MyWidgetFactory(widget, picture):
     newclass = type("WithImage" + widget.__name__, (widget,), dict(render=newrender))
     newclass.image = image
     return newclass()

但是一旦我的 newrender 方法启动,我就会收到这个错误:

result = super().render(name, value, attrs) RuntimeError: super(): __class__ cell not found

是工厂使用不当还是其他原因?

5 分钟后编辑 好的,我只需要通过调用 super(type(self), self) 来填充超级单元格。不太清楚它是如何工作的,但是,嘿,它奏效了!

谢谢大家!

【问题讨论】:

  • 可能是元类的用例
  • 自定义类的创建,通常使用metaclasses。具体应用视实际需求而定。

标签: python python-3.x oop inheritance


【解决方案1】:

这是一个字典的用例(或者可能是一个列表,如果字典键只是一个连续整数序列):

base_classes = {
    1: Component1,
    2: Component2,
    # ...
}

def ComponentFactory(comp_type):
    return base_classes(comp_type)()

在这里,ComponentFactory 为您创建实例,而不是创建一个基本上只是包装现有类而不实际添加的新类。


如果你真的需要一个新类型,你也可以在工厂中创建它:

def ComponentFactory(comp_type):
    new_type = type('SomeTypeName', (base_classes[comp_type],), {})
    return new_type()

但是,您可能应该注意,您只为每个真实类创建 一个 这样的包装器,这样您就不会得到一堆相同的单例类。

【讨论】:

  • 我了解工厂的使用,但是,如果我理解得好,我可以在工厂内部使用猴子补丁来获得所需的修改类,对吧?不过,猴子补丁在我的框架中似乎效果不佳,即使使用类的实例而不是整个类:/
  • 我会尽可能避免猴子补丁。它打破了封装,并假设您正在修补的东西可能不存在。
【解决方案2】:

第一,你不会将你的类命名为 MyComponent2112,你会有一个以类作为值的字典。更容易使用。

然后在循环中创建类:

import third_party_module

mycomponents = dict()

for componentname in dir(third_party_module):
    if not componentname.startswith('Component'): continue
    baseclass = getattr(third_party_module, componentname)
    if not issubclass(baseclass, third_party_module.VeryBaseClass): continue
    number = int(componentname[9:])

    class MyComponent(baseclass):
        # Your updates here
    mycomponents[number] = MyComponent

【讨论】:

  • 谢谢!我认为我通过使课程数量比实际更重要来误导您,但我很欣赏您的解决方案。但是,我希望将新创建的类命名为 MyClassDoesThis、MyClassDoesThat,而不是将它们放入字典中。生成动态类名的任何提示?尝试修改__name__,但之后仍然无法使用新名称调用该类:/
  • 我不喜欢动态变量名,通常你还需要奇怪的代码来动态使用它们,我想链接一篇著名的博客文章,但我现在想不起来了。
  • 无论如何你可以设置一个全局变量globals()['yourclassname'] = MyComponent
  • 如果您希望该类实际上也具有该名称(而不是所有内部都具有名称“MyComponent”),那么您需要使用type 的三参数形式,如切普纳的回答。
【解决方案3】:

如果不仔细查看代码库,就很难回答。 但是,在我看来,这似乎是一个可以通过猴子补丁解决的问题。

换句话说,您可以向 Component1、Component2、Component3 添加方法,甚至可以替换这些类的现有方法。

请记住,python 肯定是 OOP,但它还提供了一组“动态”工具(例如猴子路径),您可以在许多场景中有效地使用。

干杯

查莉

【讨论】:

  • 已经尝试过猴子修补它,但我收到有关缺少参数的错误,所以我有点放弃了这条路。如果其他解决方案不满意,我可能会再试一次!
  • 请注意,您可以对类和/或对象(也称为类的实例)进行修补。看看这里filippo.io/instance-monkey-patching-in-python。这篇文章对我很有用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-12
  • 2023-03-18
  • 2011-06-03
相关资源
最近更新 更多