Metaclasses vs. standard Python reflection?
Dave Benjamin
ramen at lackingtalent.com
Tue May 6 15:41:58 EDT 2003
In article <ZyTta.91215$K35.2547601 at news2.tin.it>, Alex Martelli wrote:
> For example, say in logger.py you only have the following code:
>
> def log_call(method):
> def new_method(self, *args, **kwds):
> print 'Entering %s...' % method
> result = method(self, *args, **kwds)
> print 'Exiting %s...' % method
>
> return new_method
Just in case another reader tries this code, I should mention that I forgot
a return statement in new_method. After that second print statement, there
should be another line that says "return result". Otherwise, this technique
will clobber all return values from methods. ;)
For the cut-and-pasters:
def log_call(method):
def new_method(self, *args, **kwds):
print 'Entering %s...' % method
result = method(self, *args, **kwds)
print 'Exiting %s...' % method
return result
return new_method
> class Logger(type):
> def __new__(cls, name, bases, dict):
> for key in dict:
> if callable(dict[key]):
> dict[key] = log_call(dict[key])
>
> return type.__new__(cls, name, bases, dict)
>
> and simple.py is unchanged (except I lowercased the filename:-).
>
> Now, for example:
>
> Python 2.3b1+ (#10, May 3 2003, 20:20:32)
> [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> import logger
>>>> import types
>>>> simple = types.ModuleType('simple') # make a new empty module
>>>> simple.__metaclass__ = logger.Logger # set its __metaclass__
>>>> execfile('simple.py', simple.__dict__) # and now populate it
>>>> simple.Simple.__class__ # check we have the metacl
><class 'logger.Logger'>
>>>> x = simple.Simple(23) # make an instance
> Entering <function __init__ at 0x402af534>...
> Exiting <function __init__ at 0x402af534>...
>>>> x.print_x() # call its method
> Entering <function print_x at 0x402af56c>...
> x = 23
> Exiting <function print_x at 0x402af56c>...
>>>> class lessimple(simple.Simple): # now let's subclass
> ... def somethingelse(self): print 'boo!'
> ...
>>>> y = lessimple(42) # subclass instance
> Entering <function __init__ at 0x402af534>...
> Exiting <function __init__ at 0x402af534>...
>>>> y.somethingelse() # and ITS method
> Entering <function somethingelse at 0x402af614>...
> boo!
> Exiting <function somethingelse at 0x402af614>...
>>>>
>
>
> Does this help...? To use the per-module __metaclass__ in a module
> that wasn't written for this, you have to use some trick such as this
> one (there may be many others too). Afterwards, __metaclass__ gets
> inherited. Remember to ALSO bind the name 'object' (to an empty
> class with your metaclass) to get classes that are newstyle because
> they inherit from object.
Yes! Thank you! =)
I managed to bind "object" this way - is this how you would do it?:
simple.object = simple.__metaclass__('object', (), {})
> This won't work for classes that inherit from other builtin types
> or declare their own __metaclass__ in their body -- THOSE you have
> to fixup post facto (with some subtlety in the latter case -- you
> will need to dynamically generate a suitable metaclass inheriting
> from the explicitly set one AND take care about conflicts in the
> intended semantics, particularly in __new__ -- indeed SOME conflicts
> might possibly be insoluble).
It also assumes you're only applying a single aspect, "Logger" in this case.
To apply multiple aspects, it would probably be a good idea to have a single
metaclass that dispatches...
Well, that was very helpful. Thanks for taking the time to answer that. =)
Take care,
Dave
More information about the Python-list
mailing list