Python __metaclass__ inheritance issue

My problem is that I use a metaclass to transfer certain class methods to a timer for logging.

For instance:

class MyMeta(type): @staticmethod def time_method(method): def __wrapper(self, *args, **kwargs): start = time.time() result = method(self, *args, **kwargs) finish = time.time() sys.stdout.write('instancemethod %s took %0.3f s.\n' %( method.__name__, (finish - start))) return result return __wrapper def __new__(cls, name, bases, attrs): for attr in ['__init__', 'run']: if not attr in attrs: continue attrs[attr] = cls.time_method(attrs[attr]) return super(MetaBuilderModule, cls).__new__(cls, name, bases, attrs) 

The problem that I am facing is that my shell works for each "__init__", although I really want it only for the current module that I am creating. The same goes for any method that time wants. I do not want time to be executed on any inherited methods unless they are overridden.

 class MyClass0(object): __metaclass__ = MyMeta def __init__(self): pass def run(self): sys.stdout.write('running') return True class MyClass1(MyClass0): def __init__(self): # I want this timed MyClass0.__init__(self) # But not this. pass ''' I need the inherited 'run' to be timed. ''' 

I have tried several things, but so far I have not been successful.

+4
source share
2 answers

Store time code with attribute. Thus, only the external method of the decorated object will be actually synchronized.

 @staticmethod def time_method(method): def __wrapper(self, *args, **kwargs): if hasattr(self, '_being_timed'): # We're being timed already; just run the method return method(self, *args, **kwargs) else: # Not timed yet; run the timing code self._being_timed = True # remember we're being timed try: start = time.time() result = method(self, *args, **kwargs) finish = time.time() sys.stdout.write('instancemethod %s took %0.3f s.\n' %( method.__name__, (finish - start))) return result finally: # Done timing, reset to original state del self._being_timed return __wrapper 

Timing for an external method only is slightly different from "methods not inherited in time if they are not redefined", but I believe that it solves your problem.

+4
source

I'm not sure if this has anything to do with multiple inheritance.

The problem is that any subclass of MyClass0 must be an instance of the same metaclass, which means that MyClass1 is created using MyMeta.__new__ , so its methods are processed and verified in the synchronization code.

Effectively, you need MyClass0.__init__ somehow return something else in the following two cases:

  • When called directly (directly creating MyClass0 or when MyClass1 does not override it), it needs to return the timed method
  • When called inside a subclass definition, it needs to return the original method is impossible

This is not possible because MyClass0.__init__ does not know why it is being called.

I see three options:

  • Make the metaclass more complicated. He can check the base classes to make sure they are already instances of the metaclass; if so, he can create a new copy that removes the temporary wrapper from the methods that are present in the constructed class. You do not want to directly modify the base classes, as this will affect their use of all (including when they are created directly or when they are subclassed by other classes that override different methods), The disadvantage of this is that it really wraps instanceof relationships; if you don’t build small variations of the base classes, creating new subclasses of them (pah!) and caching all the changes, so you never build duplicates (pah!), you completely invalidate the natural assumption that the two classes are separated by the base class (they can only use template from which two completely independent base classes were created).
  • Make synchronization code more complex. Have a start_timing and stop_timing , and if start_timing is called when the method is already synchronized, you simply increase the counter, and stop_timing just decreases the counter and stops only the time when the counter reaches zero. Be careful with timed methods that invoke other temporary methods; you will need to have separate counters for the method name.
  • Discard the metaclasses and just use the decorator for the methods you want to configure by timer, with some way to access the undecorated method so that redefinition of the definitions can cause it. This will include a pair of boiler plate lines for each use; which may add fewer lines of code than either of the other two options.
+1
source

All Articles