views:

70

answers:

2

I've got a base class where I want to handle __add__() and want to support when __add__ing two subclass instances - that is have the methods of both subclasses in the resulting instance.

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

Edit: To be more specific: I've got a class Hosts that holds a dict(host01=.., host02=..) (hence the subclassing of dict) - this offers some base methods such as run_ssh_commmand_on_all_hosts()

Now I've got a subclass HostsLoadbalancer that holds some special methods such as drain(), and I've got a class HostsNagios that holds some nagios-specific methods.

What I'm doing then, is something like:

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

What is the best solution for this problem?

I think I can somehow "copy all methods" - like this: for x in dir(other): setattr(self, x, getattr(other, x))

Am I on the right track? Or should I use Abstract Base Classes?

+1  A: 

In general this is a bad idea. You're trying to inject methods into a type. That being said, you can certainly do this in python, but you'll have to realize that you want to create a new type each time you do this. Here's an example:

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__

UPDATE I've updated the example to remove manually moving the methods -- we're using mixins here.

Mike Axiak
nice, didn't know about `type('A_B', (A, B), {})` works - that looks very much what I looked for - internally we're still discussing if my general approach is correct though ;-)
Philipp Keller
A: 

It is difficult to answer your question without more information. If Base is supposed to be a common interface to all classes, then you could use simple inheritance to implement the common behavior while preserving the methods of the subclasses. For instance, imagine that you need a Base class where all the objects have a say_hola() method, but subclasses can have arbitrary additional methods in addition to 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'

This way all instances of C1 and C2 have say_hola() in addition to their specific methods.

A more general pattern is to create a Mixin. From Wikipedia:

In object-oriented programming languages, a mixin is a class that provides a certain functionality to be inherited by a subclass, while not meant for instantiation (the generation of objects of that class). Inheriting from a mixin is not a form of specialization but is rather a means of collecting functionality. A class may inherit most or all of its functionality from one or more mixins through multiple inheritance.

Arrieta