On Sun, 11 Oct 2015 04:02 am, speeze.pear...@gmail.com wrote: > (This is a long post, but the question is simple. Most of this is > just me enumerating what I've already tried.) > > Someone wrote a library that creates and manipulates `Node`s. > I would like to write another layer on top of this, to make > trees that behave just like the library's trees, but whose nodes > have some extra methods. > > "Subclass!" is the classic OO answer, and my first instinct. > Problem: I never instantiate `Node` myself. `Node`s are created > somewhere deep inside the library, and there's no way to tell it > "don't use `Node`, use this class instead."
But you have trees of nodes returned? Here are a few solutions: # Solution 1: inject a new method into each and every instance in the tree. # Does not work for __dunder__ methods, but should work for any class that # includes a per-instance __dict__ (that's most classes apart from # built-ins). from types import MethodType def foo(self): return self.spam or self.eggs for node in the_tree: node.foo = MethodType(foo, node) # Later... for node in the_tree: print node.foo() # Solution 2: hack the node type of each instance. # Should work fine for adding new methods, even __dunder__ methods. # Depending on the original class, may or may not work for overriding # existing methods. class MyNode(Node): def foo(self): return self.spam or self.eggs for node in the_tree: node.__class__ = MyNode # Later... for node in the_tree: print node.foo() Solution #2 is especially good to test your team's internal procedures and code review practices. Any code review which doesn't flag it as a "What the hell are you doing??? Is this even legal???" change is clearly deficient. Yes, it is legal, in fact it is not just legal but a deliberately supported feature of Python. But it is a bit tricky to get the details right. As written above, it should work, but for me to really be comfortable with using this in production, I would want to have written the original class specifically with this in mind. While both of the above have their uses, really the *right* way to do this is to stop being such an OOP purist. This isn't Java, we have functions for a reason. # Solution 3: just use a damn function. Works for any class, whether pure # Python or an extension class. Doesn't require any special support in the # original class, and guaranteed not to give your code reviewers a # conniption fit. def foo(node): return node.spam or node.eggs # Later... for node in the_tree: print foo(node) > Does anyone know a programming pattern that solves this problem? > > (That's it! That's the question. Everything after this is > descriptions of solutions I'm not happy enough with.) > > "Write a wrapper!" is my next instinct. Basically, > > class MyNode(object): > ... > def foo(self, *args, **kwargs): > return self.node.foo(*args, **kwargs) > def bar(self, *args, **kwargs): > return self.node.bar(*args, **kwargs) > ... > > This would be tedious, but acceptable, if I knew how to make > `MyNode.parent` and `MyNode.children` return `MyNode`s > instead of `Node`s. class MyNode(Node): @property def parent(self): parent = super(MyNode, self).parent if parent is not None: parent = MyNode(parent) return parent # etc. Still a lot of work, and you really need to understand the original Node class very well. This is the problem with subclassing: it requires you to be intimately familiar with the *implementation details* of the class you subclass. > My last idea is that my approach is *entirely* wrong: I shouldn't > be trying to make the tree's nodes instances of my own class -- > I should just use the existing library's `Node` class, and write > my library in a functional style. Don't define `MyNode.foo()`, > instead define `mylibrary.foo(my_node)`. > I've got nothing against functional programming, but mixing it > so closely with OO would, I think, result in a confusing, > schizophrenic interface. Do you find len(alist) to be confusing and schizophrenic? > Last possibly relevant fact: the library I want to extend is > `xml.dom.minidom`. By `Node` I mean `Element`, and the specific > methods I want to add will modify attributes used for CSS/JavaScript. It may be relevant. Try it and see. -- Steven -- https://mail.python.org/mailman/listinfo/python-list