How do I extend a class that I never instantiate myself?

Ian Kelly ian.g.kelly at gmail.com
Sat Oct 10 13:39:01 EDT 2015


On Sat, Oct 10, 2015 at 11:02 AM,  <speeze.pearson at 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."
>
> Does anyone know a programming pattern that solves this problem?

The factory method pattern would be the usual solution, but it depends
on the library being written to support it.

Alternatively you could monkey-patch the Node class in the library
(with all the usual caveats about why you probably shouldn't).

> (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. Every way I've thought of makes me cringe:
> - Make `MyNode` maintain its own parent/child info, and keep it
>   in lock-step with the underlying `Node`'s parent/child info.
>   Objection: this is a lot of work, and just begging to get out of sync.
> - Give every wrapped `Node` a `_wrapper` attribute, specifying
>   the wrapping `MyNode` instance. Then `MyNode.children` would
>   return `[child._wrapper for child in self._node.children]`.
>   Objection: it just feels weird adding custom attributes to objects.

If you do this, I would suggest calling the attribute __wrapper (with
two underscores) to invoke name mangling in order to ensure that the
attribute doesn't conflict with any added by the library itself.

> - Have a global registry mapping `Node`s to their corresponding
>   `MyNode` wrappers. Then `MyNode.children` would return
>   `[MyNode.wrappers[child] for child in self.node.children]`.
>   Objection: I've been trained not to like global state.

Implement MyNode.parent and MyNode.children as properties that wrap
the underlying Node's parent/children on demand. This means you lose
consistency of object identity for the wrappers, but maybe that's not
important to you. If you want to maintain that, then cache the
wrappers, perhaps using the __wrapper attribute scheme above.

> 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.

There's nothing wrong with this IMO. In fact, Python does this in the
standard library, e.g. len(objects) rather than objects.len().



More information about the Python-list mailing list