Changing base class of a big hierarchy

John J. Lee jjl at pobox.com
Sun Aug 3 13:43:55 EDT 2003


Alex Martelli <aleax at aleax.it> writes:

> John J. Lee wrote:
> 
> > I'm trying to change a base class of a big class hierarchy.  The
> > hierarchy in question is 4DOM (from PyXML).  4DOM has an FtNode class
> > that defines __getattr__ and __setattr__ that I need to override.
[...]
> OK, so you want to do two things:
>   -- "deep-copy" a class hierarchy H to H' 
>   -- in the copied hierarchy H', change each occurrence in any __bases__
>      of a class X' to a class Y', and any other class T that is in H to
>      the corresponding class T' in H'

Yes.


> > What's the easiest way to do that?
> 
> I think performing these two steps in order is probably simplest.  You
> do have to identify every class in your starting hierarchy H, of course.
> 
> > Maybe deep-copying the module or something, then fiddling with
> > __bases__??  Or a metaclass?
> 
> Deep copying might not help -- copy.deepcopy(X), when X is a class,
> still leaves __bases__ unchanged.  A custom metaclass that does

:-(


> bases-alteration as you require would be reasonably easy to write,
> but would be more helpful than just doing the same job in a
> function if, and only if, you were to re-execute every affected
> 'class' statement using the new metaclass.  I doubt that getting
> and re-executing those class statements is simplest in this case.

So I do have to fiddle with __bases__ after all, contrary to my
follow-up to my OP.  In fact:

>>> class bar: pass
>>> bar2 = copy.copy(bar)
>>> bar2 is bar
True
>>> bar3 = copy.deepcopy(bar)
>>> bar3 is bar
True
>>>

(because the registered class-copying functions for copy and deepcopy
just return the original object).  I suppose I have to use

new.classobj(klass.__name__, bases, klass.__dict__)


> > While I'm on the subject, does anybody have code to get a list of all
> > classes in a package that derive from a particular class?  Or
[...]
> from X, so you'd filter that with issubclass); but offhand I can't
> think of a handy, elegant and robust way to ensure you're walking
> all over a package (including sub-packages), nor even that all of
> a package (cum subpackages) is actually LOADED at this time.  I

That's a shame.  Still, for my particular case, I think I can get a
list of the relevant classes using some package-specific data, a bit
of string processing and a few hard-coded cases.


[...]
> # seed this with the base class transformation
> trans_dict = {X: transformed_X}
> # recursive way to transform a class between hierarchies
> def transform_class(C):
>     if not issubclass(C, X):
>         return C
>     if C in trans_dict:
>         return trans_dict(C)
>     bases = tuple([transform(B) for B in C.__bases__])
>     newclass = type(C)('C', bases, C.__dict__)
>     trans_dict[C] = newclass
>     return newclass

Thanks.  That's simpler than I expected.  The type(C)(...) there is
equivalent to my new.classobj, I see (except you wrote 'C' when you
meant C.__name__).  I suppose one could then write a deepcopying
function _deepcopy_cls, similar to your function (and analogous to
copy._deepcopy_inst(x, memo)), and then create one's own deepcopy
function essentially identical to copy.deepcopy, except that it uses
_deepcopy_cls(x, memo).  In my case, I think I can just use your
function directly in 4DOM's

HTMLDocument._4dom_createHTMLElement(self, tagName)


Side issue: I wonder why new.classobj is there, given that even in
1.5.2 ClassType(name, bases, type) does exactly the same?  I guess
new.classobj predates ClassType.


> Of course, if the classes in the hierarchy use each other in
> more ways than just a base/subclass inheritance [e.g, some
> classes have other classes as attributes, not just as bases]
> you may need a bit more work [e.g., on C.__dict__ as well as
> on C.__bases__].

Hadn't thought of that.  Maybe I should write a _deepcopy_cls after
all.  I'll have to check whether 4DOM uses classes as attributes.


[...]
> to get the class-objects it uses.  You may need to alter the
> semantics of 'import' statements in that using-code, too, since

Thankfully not, in 4DOM, since it calls the method I mentioned above
to get a new Element (the implementation uses __import__).


John




More information about the Python-list mailing list