2011-10-11 82 views
4

我的问题是,我正在使用一个元类来包装某些类的方法在一个计时器记录的目的。Python __metaclass__继承问题

例如:

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) 

我遇到的问题是,我的包装,每运行“__init__”即使我真的只是想对当前模块我实例化。任何想要时间的方法都是一样的。我不想让时间在任何继承的方法上运行,除非它们没有被覆盖。

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. ''' 

我试过一些东西,但到目前为止我没有成功。

回答

4

用属性保护计时代码。这样,只有对象上最外面的装饰方法才会实际得到时间。

@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 

计时只有最外层的方法比“除非他们没有被覆盖不定时继承的方法”略有不同,但我相信它可以解决你的问题。

+0

谢谢佩特,这工作完美。我非常接近我能够品尝到的答案。有时你需要另一双眼睛来理解我所猜测的疯狂。再次感谢! CB – theceebee

1

我不确定这与多继承有什么关系。

麻烦的是,的MyClass0任何子类必须是相同的元类,这意味着MyClass1获取与MyMeta.__new__创建的一个实例,因此它的方法得到处理和包裹在所述定时码。

实际上,你需要的是一个MyClass0.__init__莫名其妙地返回的东西在下面两种情况下的不同:

  1. 当直接调用(实例MyClass0直接,或当MyClass1不覆盖它),它需要返回定时方法
  2. 当一个子类定义中调用,它需要返回原来的不计时方法

这是不可能的,因为MyClass0.__init__不知道为什么它被称为。

我看到三个选项:

  1. 让元类更复杂。它可以通过基类来检查它们是否已经是元类的实例;如果是这样,它可以创建它们的新副本,从正在构建的类中存在的方法中移除定时包装器。您不希望直接对基类进行变异,因为这会影响它们的使用(包括直接实例化或其他类覆盖不同方法的子类时)。它的缺点是它真的拧紧instanceof关系;除非你通过创建它们的新子类(ugh!)并缓存所有的变体来构造基类的细微变化,所以你永远不会构造重复项(ugh!),你完全无法自然假设两个类共享一个基类(它们可能只共享两个完全独立的基类生成的模板)。
  2. 使时间码更复杂。有一个start_timingstop_timing方法,并且如果start_timing在方法已被定时时调用,则只需增加一个计数器,而stop_timing只是递减一个计数器,并且只在计数器达到零时停止计时。注意调用其他定时方法的定时方法;您需要为每个方法名称分配不同的计数器。
  3. 放弃元类,只是在你想要显式定义的方法上使用装饰器,以某种方式获取undecorated方法,以便覆盖的定义可以调用它。这将涉及几行锅炉板每次使用;那很可能会比其他两个选项中的代码行少加起来。
+0

感谢您的帮助,我将与第2号一起去。我希望尽可能让代码尽可能方便人们阅读,但仍然强制时间/日志记录发生,所以我觉得元类路由是最好的方式。使用装饰器依赖于继承/抽象类来实际装饰他们的方法的人,因为我必须假定只有基本的Python技能,所以尽可能自动化是最好的方式。再次感谢! CB – theceebee