The ObjectMerger class dynamically merges two given objects, making one a subclass of the other. The input elements could either be class instances or simple types, making this class particularly useful to derive native classes (for instance, cStringIO). The resulting ObjectMerger instance acts as a proxy to the new type, allowing callers to work with it in the same way they would work with any other statically derived type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #
# version: 1 (2004-03-05)
#
class ObjectMerger:
"""
Object 'Merger'.
Returns an instance that takes methods from two given instances/objects.
In order, as if one have the subclass methods of the other.
"""
def __init__(self, super, _self):
self.___super = super
self.___self = _self
def __getattr__ ( self, name ):
if not name.startswith('___'):
super = self.___super
_self = self.___self
if hasattr(_self, name):
return getattr(_self, name)
elif hasattr(super, name):
return getattr(super, name)
return getattr(ObjectMerger, name)
if __name__=='__main__':
import unittest
class test1:
def a(self): pass
def b(self): pass
class test2:
def a(self): pass
def c(self): pass
class testObjectMerger(unittest.TestCase):
def test_merged_instances_method_priority (self):
"""Test priority of methods"""
c1 = test1()
c2 = test2()
m = ObjectMerger(c1, c2)
self.assertEqual(m.a, c2.a)
self.assertEqual(m.b, c1.b)
self.assertEqual(m.c, c2.c)
def test_merged_objects_with_one_noninstance (self):
"""Test mix of instance and object(string)"""
c1 = test1()
c2 = 'hola carlos'
m = ObjectMerger(c1, c2)
self.assertEqual(m.a, c1.a)
#can't compare functions directly because:
# 1. it seems that after first get function is copied..
# or something
# 2. don't know why python fails comparing the function..
self.assertEqual(m.__len__(), c2.__len__())
self.assertEqual(m.b, c1.b)
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(testObjectMerger))
unittest.TextTestRunner(verbosity=2).run(suite)
|
I used it to merge aSocket with ssl(aSocket).. giving an ssl_socket with close(). :-)
I choose this, because didn't want to make classes in runtime.
ToDo / Bugs / Improvements:
__str__ and __repr__ functions.
I think this code is not scalable, i.e.:
m = ObjectMerger(ObjectMerger(a,b),c),
but I didn't try that.. in any case, may be possible to setup internal instances with a name like id(self)_super ..
Memorization of attributes, for eliminate one indirection. Something like:
def __getattr__(self, name): .. attr = getattr(..) eval('A.%s = B'%, {'A':self, 'B':attr})
but i'm not sure if this works for nonfunction parameters.. and, also, could have unexpected behaviour.
Maybe usefull some method name overwrite, when you want to keep one interface and the subclass changes the names. I didn't wrote that. But could be usefull. For example, to do: ObjectMerger(aSocket, ssledSocket, {'recv':'read', 'send':'write'} )
This prevents getting any attribute of its instances whose name starts with ___ ( 3 x '_' )
From the instance returned you could write methods calling the 'superclass' using the instance variable self.___super.
scalling fix. To make scalling works, this patch should be added:
What about more than two objects??? Maybe you could change it to:
I believe this will allow you to fuse the attributes of any number of classes, without the explicit need for functional composition.
Here's a different version which is a bit more minimal, but may also be more efficent:
Dave
Oops... Should have used __getattribute__ on this last example (for new style classes).
Thanks. I'll try that with Javier's line too.
I prefer using hasattr() instead of catch the exception, but don't know what is better of both ways.
Thanks!
Try/Except vs. using hasattr(). I believe that using try/except is superior to testing with hasattr. Because it involves only one test (instead of using two), the code should execute faster.
There's nothing wrong with using hasattr() in principle, but I chose to use the try/except mechanism as a way of improving the performance of the algorithm, especially if it is searching through an arbitrary number of classes/instances.