当调用另一个方法时,Python类方法运行 [英] Python class method run when another method is invoked

查看:147
本文介绍了当调用另一个方法时,Python类方法运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类如下:

class MyClass(object):
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"
        self.methodCalls = 0  #tracks number of times any function in the instance is run

    def get_foo(self):
        addMethodCall()
        return self.foo

    def get_bar(self):
        addMethodCall()
        return self.bar

    def addMethodCall(self):
        self.methodCalls += 1

是否存在每次调用方法时都会调用的内置函数,而不是不断运行 addMethodCall()

Is there an inbuilt function that is invoked whenever a method is invoked instead of constantly running addMethodCall()?

推荐答案

不,在课程上没有钩子。方法也是属性 ,尽管有些特殊,因为它们是在访问实例上的函数对象时产生的。函数是描述符

No, there are no hooks on a class to do this. Methods are attributes too, albeit somewhat special in that they are produced when accessing the function object on the instance; functions are descriptors.

对方法对象的调用然后是生成方法对象的单独步骤:

The call to a method object is then a separate step from producing the method object:

>>> class Foo(object):
...     def bar(self):
...         return 'bar method on Foo'
...
>>> f = Foo()
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x100777bd0>>
>>> f.bar is f.bar
False
>>> stored = f.bar
>>> stored()
'bar method on Foo'

这是 object .__ getattribute __()方法调用描述符协议,因此您可以加入该协议以查看何时生成方法,但是您仍然需要 wrap 包装生成的方法对象以检测 calls 。您可以返回 __ call __ 方法代替实际方法。

It is the task of the object.__getattribute__() method to invoke the descriptor protocol, so you could hook into that to see when a method is produced, but you'd still need to wrap that produced method object to detect calls. You could return an object with a __call__ method that proxies for the actual method for example.

但是,使用增加a的装饰器装饰每个方法会更容易。每次调用计数器。考虑到装饰器应用于绑定的函数之前,因此您必须将 self 传递给:

However, it'd be easier to decorate each method with a decorator that increments a counter every time it is called. Take into account decorators apply to a function before it is bound, so you'll have to pass self along:

from functools import wraps

def method_counter(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        self.methodCalls += 1
        return func(self, *args, **kwargs)
    return wrapper

您仍然需要将此值应用于类中的所有函数。您可以将其手动应用于要计数的所有方法:

You'd still need to apply this to all functions in your class. You could apply this manually to all methods you want to count:

class MyClass(object):
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"
        self.methodCalls = 0  #tracks number of times any function method is run

    @method_counter
    def get_foo(self):
        return self.foo

    @method_counter
    def get_bar(self):
        return self.bar

或者您可以使用元类:

import types

class MethodCounterMeta(type):
    def __new__(mcls, name, bases, body):
        # create new class object
        for name, obj in body.items():
            if name[:2] == name[-2:] == '__':
                # skip special method names like __init__
                continue
            if isinstance(obj, types.FunctionType):
                # decorate all functions
                body[name] = method_counter(obj)
        return super(MethodCounterMeta, mcls).__new__(mcls, name, bases, body)

    def __call__(cls, *args, **kwargs):
        # create a new instance for this class
        # add in `methodCalls` attribute
        instance = super(MethodCounterMeta, cls).__call__(*args, **kwargs)
        instance.methodCalls = 0
        return instance

这可以满足装饰器的所有需求,为您设置 methodCalls 属性,因此您的课程不必:

This takes care of everything the decorator needs, setting a methodCalls attribute for you, so your class doesn't have to:

class MyClass(object):
    __metaclass__ = MethodCounterMeta
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"

    def get_foo(self):
        return self.foo

    def get_bar(self):
        return self.bar

后一种方法的演示:

>>> class MyClass(object):
...     __metaclass__ = MethodCounterMeta
...     def __init__(self):
...         self.foo = "foo"
...         self.bar = "bar"
...     def get_foo(self):
...         return self.foo
...     def get_bar(self):
...         return self.bar
...
>>> instance = MyClass()
>>> instance.get_foo()
'foo'
>>> instance.get_bar()
'bar'
>>> instance.methodCalls
2

上述元类仅考虑函数对象(因此 def 语句和 lambda 表达式的结果)是装饰的类主体的一部分。它忽略任何其他可调用对象(存在更多具有 __ call __ 方法的类型,例如 functools.partial 对象) ,以及添加到类稍后的函数。

The above metaclass only considers function objects (so the result of def statements and lambda expressions) part of the class body for decoration. It ignores any other callable objects (there are more types that have a __call__ method, such as functools.partial objects), as are functions added to the class later on.

这篇关于当调用另一个方法时,Python类方法运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆