python: 基类中的多重继承和__add__()

3 投票
2 回答
1802 浏览
提问于 2025-04-16 04:14

我有一个基础类,想要处理 __add__() 方法,并且希望在把两个子类实例相加的时候,结果实例能包含这两个子类的方法。

import copy

class Base(dict):
    def __init__(self, **data):
        self.update(data)

    def __add__(self, other):
        result = copy.deepcopy(self)
        result.update(other)
        # how do I now join the methods?
        return result

class A(Base):
    def a(self):
        print "test a"

class B(Base):
    def b(self):
        print "test b"


if __name__ == '__main__':
    a = A(a=1, b=2)
    b = B(c=1)
    c = a + b
    c.b() # should work
    c.a() # should work

编辑: 更具体一点说,我有一个类 Hosts,它里面存放了一个 dict(host01=.., host02=..)(所以我让它继承了 dict) - 这个类提供了一些基础方法,比如 run_ssh_commmand_on_all_hosts()

现在我有一个子类 HostsLoadbalancer,它里面有一些特别的方法,比如 drain(),还有一个类 HostsNagios,它里面有一些与 nagios 相关的方法。

我现在做的事情大概是这样的:

nagios_hosts = nagios.gethosts()
lb_hosts = loadbalancer.gethosts()
hosts = nagios_hosts + lb_hosts
hosts.run_ssh_command_on_all_hosts('uname')
hosts.drain() # method of HostsLoadbalancer - drains just the loadbalancer-hosts
hosts.acknoledge_downtime() # method of NagiosHosts - does this just for the nagios hosts, is overlapping

那这个问题的最佳解决方案是什么呢?

我觉得我可以以某种方式“复制所有方法” - 比如这样: for x in dir(other): setattr(self, x, getattr(other, x))

我这样做对吗?还是说我应该使用抽象基类(Abstract Base Classes)?

2 个回答

0

要回答你的问题,首先需要更多的信息。如果Base是所有类的一个公共接口,那么你可以通过简单的继承来实现共同的行为,同时保留子类的方法。举个例子,假设你需要一个Base类,里面的所有对象都有一个say_hola()的方法,但子类可以在say_hola()之外,添加任意其他的方法:

class Base(object):
   def say_hola(self):
     print "hola"

class C1(Base):
   def add(self, a, b):
      return a+b

class C2(Base):
   def say_bonjour(self):
      return 'bon jour'

这样,所有的C1C2的实例除了有各自特定的方法外,还会有say_hola()这个方法。

还有一种更通用的做法是创建一个Mixin。根据维基百科的解释:

在面向对象编程语言中,Mixin是一种类,它提供某种功能,可以被子类继承,但本身并不用于生成对象(也就是不直接创建这个类的实例)。从Mixin继承并不是一种特殊化,而是一种收集功能的方法。一个类可以通过多重继承,从一个或多个Mixin中继承大部分或全部功能。

1

一般来说,这样做是不太好的主意。你是在尝试把方法加到一个类型里。虽然在Python中确实可以这样做,但你要明白,每次这样做的时候其实是想创建一个新的类型。下面是一个例子:

import copy

class Base(dict):
    global_class_cache = {}

    def __init__(self, **data):
        self.local_data = data

    def __add__(self, other):
        new_instance = self._new_type((type(self), type(other)))()
        new_instance.update(copy.deepcopy(self).__dict__)
        new_instance.update(copy.deepcopy(other).__dict__)
        return new_instance

    def _new_type(self, parents):
        parents = tuple(parents)
        if parents not in Base.global_class_cache:
            name = '_'.join(cls.__name__ for cls in parents)
            Base.global_class_cache[parents] = type(name, parents, {})
        return Base.global_class_cache[parents]

class A(Base):
    def a(self):
        print "test a"

class B(Base):
    def b(self):
        print "test b"


if __name__ == '__main__':
    a = A(a=1, b=2)
    b = B(c=1)
    c = a + b
    c.b() # should work
    c.a() # should work
    print c.__class__.__name__

更新 我更新了这个例子,去掉了手动移动方法的部分——我们这里使用的是混入(mixins)。

撰写回答