Task:
call the original class-method extended by another class-method in a subclass.
Logical Answer:
use the super function.
The Problem:
considering that the way "super" is called, is not the most beautiful feature of the Python language (this is sed to be a wart by some). this situation created a code far uglier. to be honest, I hate this solution but I can't seem to come up with a better one... does anyone have any suggestions?
the problem is that there are situations (though quite rare, or even unlikely) when this code will break!
see the example below for more details...
a full example:
class SomeBaseClass(object): ''' ''' def meth(self): ''' ''' print '###', self, self.__class__ class _MetaClass(SomeBaseClass, type): ''' ''' def __init__(cls, name, bases, ns): ''' ''' if 'meth' in ns: setattr(cls, 'meth', classmethod(ns['meth'])) super(_MetaClass, cls).__init__(name, bases, ns) class SomeClass(object): ''' ''' __metaclass__ = _MetaClass class SomeOtherClass(object): ''' ''' __metaclass__ = _MetaClass def meth(cls): ''' ''' print 'this is a class-method...' # and here is the problematic line of code: super(cls.__class__, cls).meth() # or super(cls.__metaclass__, cls).meth() if __name__ == '__main__': basic_obj = SomeBaseClass() some_other_obj = SomeOtherClass() basic_obj.meth() SomeClass.meth() some_other_obj.meth() # vim: set ts=4 sw=4 nowrap :
metaclass_example.py
if anyone is interested, this is a simplified version of the structure of a module (pli.event.event or a shorthand pli.event), part of the pli library.
July 17 2004, 15:02:35 UTC 7 years ago
(an hour later)
After spending time reading up on metaclasses and looking at what you are doing, I've come full circle and once again decided that metaclasses offer me nothing that I would really want to do (I've never used __metaclass__, classmethod or staticmethod in any production code).
Now, it seems as though the code you offer does what you want it to, so I say, if it works, make it into a recipe, post it in the Python cookbook. Speaking of which, there are a pair of recipes that seem to do similar things to what you want, but are easily used by other code (doesn't need to be reimplemented every time).
http://aspn.activestate.com/ASPN/Cookbo
http://aspn.activestate.com/ASPN/Cookbo
July 19 2004, 09:05:41 UTC 7 years ago
part #1
this is not a solution, though it does do what it was intended to do to some extent...the problem here is the same as with "super(self.__class__, self).meth()", that is, as soon as you change, or combine metaclasses for some child class, you'll get wrong method resolution, and most likely fall into infinite recursion... (see example below).
the simplest solution would be to pass the metaclass to super explicitly, but this is even worse than passing the class explicitly in the general case, as the class and the metaclass can be located in different modules/packages... etc. I can't say that this will contribute to code clarity :)
NOTE: this message is split into two posts due to its length...
July 19 2004, 09:16:21 UTC 7 years ago
part #2 -- the example.
(continued...)example:
metaclass_example_killer.py
P.S. I made a little typo in the module name in the main post (the word example was lacking the "p"), now this is corrected... :)
July 23 2004, 00:45:18 UTC 7 years ago
Re: part #2 -- the example.
While I haven't run your second example (I've been strapped for time lately), your description seems to show that metaclasses suffer from the same "which method did you want" syndrome that occurs when using multiple inheritance on regular classes. Though I could have missed something.Generally what is done with regular classes is to be explicit about what you want, because with multiple inheritance, sometimes it is ambiguous as to precisely what you want.
July 23 2004, 01:46:36 UTC 7 years ago
Re: part #2 -- the example.
well, I have not stumbled on this syndrome myself, though at the course I teach at the MSU I had to be quite clear on the matter. as this is quite an easy mistake to make, and rather hard one to find...first, it is not recommended to use the self.__class__ in the super function as this always resolves to the current objects' class, and this is not what we want in most cases. consider the following code:
what happened above is quite clear, the problem lies in the line "super(self.__class__, self).foo()", where instead of calling the parents foo method (relative to c) we call the current method. this is because self.__class__ relative to the object of c resolves correctly (to the class "c"), and relative to any object of a subclass of "c" it will resolve to the immediate class of that object no matter what "foo" are we calling...
here is a version of your code to avoid this syndrome, showing the way methods are resolved (mro) in python2.3
NOTE: this code is a bit overly pedantic in terms of base class reusability (the try blocks can be skipped if you know that there is to be no other inheritance scheme other than the one used).
now you can use the classes "a" and "b" in any combination and order....
also note that if one desires a particular call order, or partial calls the only thing to do is to explicitly call each method needed...
July 23 2004, 02:08:44 UTC 7 years ago
Re: part #2 -- the example.
there actually almost no difference between regular classes and metaclasses, all the same problems, tricks and techniques apply... the only difficulty is that most people try to think of objects, classes and metaclasses all at once... (concept stew!)almost all the differences and inconsistencies that exist in Python, are a result of optimizations and legacy code... (the legacy part of Python I really hate! :)) )
for example, you can change the class of an object, but you can not change the class of a class... :))
// to see an example of this put to good use, see the pli.pattern.state and
// pli.pattern.fsm modules (I gave the the link above)...
// ...though note that the pli.pattern.fsm is far from finished... :))