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