class Prototype(object):
def __init__(self, *args):
self._parents = []
for each in args:
self._parents.append(each)
def clone(self, *args):
return self.__class__(self, *args)
def __getattr__(self, name):
for each in self._parents:
try:
return getattr(each, name)
except AttributeError:
pass
raise AttributeError(name)
The implementation is suprisingly simple: three methods!
The first is the initialize method for new instances. It takes a list of objects that we will use as the parents of our new object. Of course, by simply calling Prototype's constructor with no arguments, we get a clean object. I made the ability to have multiple inheritance simple because of Self.
The second method is the clone method and it simply calls the constructor for another object makes itself the first parent with the rest of the arguments becoming the other parents. Nothing to it!
The last method is the real meat. It only get called by the Python engine when an attribute fails to be looked up. All I do is call each parent and if one succeeds, it returns the answer. This is a depth first search of the parent hierarchy. Simple.
And that's all we need to implement prototypes in Python. Now, I just need to explain some practical uses of this in some later blog entries.
How do I know all of this works? Here's my tests:
import unittest
class Test(unittest.TestCase):
def testSimple(self):
parent = Prototype()
child = parent.clone()
parent.value = 0
self.assertEquals(0, child.value)
child.value = 1
self.assertEquals(1, child.value)
self.assertEquals(0, parent.value)
self.failUnlessRaises(AttributeError, lambda:parent.unknown)
def testMultipleParents(self):
parent1 = Prototype()
parent2 = Prototype()
child = parent1.clone(parent2)
parent2.value = 2
self.failUnlessRaises(AttributeError, lambda:parent1.value)
self.assertEquals(2, child.value)
If anyone sees anything wrong in my implementation or un-Pythonic let me know. I'm still learning, but I thought it would be cool to show my progress.
4 comments:
You can actually shorten your constructor down to self._parents = list(args).
Welcome to the world of Python. You will like it here.
You shouldn't copy objects so deeply. You have to make a class-like thing. You create a new object with a proto attribute. Then when an attribute is requested you find it in the local attributes. If it's not found you search in the proto, and so on until the proto is None. Your way you always copy the whole object, and it becomes heavy in memory terms.
Thanks Kesmit and MikeHoss!
To Luca, Where am I copying deeply? The clone method creates a brand new object and it is not a copy. The parents attribute is the proto attribute you were talking of. The __getattr__ is only called when an attribute is not found and then it searches for it in the parents (and if they are Prototypes, then they will do the same.
How am I copying the entire object? I want to make sure I'm not doing something wrong.
Post a Comment