python: 基类中的多重继承和__add__()
我有一个基础类,想要处理 __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 个回答
要回答你的问题,首先需要更多的信息。如果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'
这样,所有的C1
和C2
的实例除了有各自特定的方法外,还会有say_hola()
这个方法。
还有一种更通用的做法是创建一个Mixin
。根据维基百科的解释:
在面向对象编程语言中,Mixin是一种类,它提供某种功能,可以被子类继承,但本身并不用于生成对象(也就是不直接创建这个类的实例)。从Mixin继承并不是一种特殊化,而是一种收集功能的方法。一个类可以通过多重继承,从一个或多个Mixin中继承大部分或全部功能。
一般来说,这样做是不太好的主意。你是在尝试把方法加到一个类型里。虽然在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)。