Python __metaclass__ 继承问题

发布于 2024-12-09 04:01:27 字数 1270 浏览 0 评论 0原文

我的问题是,我正在使用元类将某些类方法包装在计时器中以用于记录目的。

例如:

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

我尝试了一些方法,但到目前为止还没有成功。

My issue is that I am using a metaclass to wrap certain class methods in a timer for logging purposes.

For example:

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 I'm having is that my wrapper runs for every '__init__' even though I really only want it for the current module I am instantiating. The same goes for any method want to time. I dont want the timing to run on any inherited methods UNLESS they aren't being 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've tried a few things but so far I've had no success.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我很OK 2024-12-16 04:01:27

使用属性保护计时代码。这样,只有对象上最外层的修饰方法才会真正计时。

@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

仅对最外面的方法进行计时与“不对继承的方法进行计时,除非它们没有被覆盖”略有不同,但我相信它可以解决您的问题。

Guard the timing code with an attribute. That way, only the outermost decorated method on an object will actually get timed.

@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 only the outermost method is slightly different than “not timing inherited methods unless they aren't being overridden”, but I believe it solves your problem.

冷情妓 2024-12-16 04:01:27

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

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

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

  1. 直接调用时(直接实例化 MyClass0 ,或当 MyClass1 code> 不覆盖它),它需要返回定时方法
  2. 当在子类定义中调用时,它需要返回原始的不定时方法

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

我看到三个选项:

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

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

The trouble is that any subclass of MyClass0 has to be an instance of the same metaclass, which means MyClass1 gets created with MyMeta.__new__, so its methods get processed and wrapped in the timing code.

Effectively, what you need is that MyClass0.__init__ somehow returns something different in the two following circumstances:

  1. When called directly (instantiating MyClass0 directly, or when MyClass1 doesn't override it), it needs to return the timed method
  2. When called within a subclass definition, it needs to return the original untimed method

This is impossible, since MyClass0.__init__ doesn't know why it's being called.

I see three options:

  1. Make the metaclass more complex. It can check through the base classes to see if they're already instances of the metaclass; if so it can make a new copy of them that removes the timed wrapper from the methods that are present in the class being constructed. You don't want to mutate the base classes directly, as that will affect all uses of them (including when they're instantiated directly, or when they're subclassed by other classes that override different methods). A downside of this is it really screws up the instanceof relationships; unless you construct the slight variations on the base classes by creating new subclasses of them (ugh!) and caching all the variations so you never construct duplicates (ugh!), you completely void natural assumptions that two classes share a base class (they may only share a template from which two completely independent base classes were generated).
  2. Make the timing code more complex. Have a start_timing and stop_timing method, and if start_timing is called when the method is already being timed you just increment a counter, and stop_timing just decrements a counter and only stops timing when the counter hits zero. Be careful of timed methods that call other timed methods; you'll need to have separate counters per method name.
  3. Give up on metaclasses and just use a decorator on the methods you want timed explicitly, with some way of getting at the undecorated method so that overriding definitions can call it. This will involve a couple of lines of boiler plate per use; that will quite possibly add up to less lines of code than either of the other two options.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文