Python __metaclass__继承问题

时间:2011-10-11 00:44:48

标签: python inheritance 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. '''

我尝试过一些事情,但到目前为止我还没有成功。

2 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:1)

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

麻烦的是MyClass0的任何子类都必须是同一元类的实例,这意味着MyClass1是用MyMeta.__new__创建的,所以它的方法被处理并包装在时间码。

实际上,你需要的是MyClass0.__init__在以下两种情况下以某种方式返回不同的东西:

  1. 直接调用(直接实例化MyClass0,或MyClass1不覆盖它时),需要返回定时方法
  2. 在子类定义中调用时,需要返回原始的不定时方法
  3. 这是不可能的,因为MyClass0.__init__不知道为什么被调用。

    我看到三个选项:

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