gpt4 book ai didi

Python 类方法在调用另一个方法时运行

转载 作者:太空宇宙 更新时间:2023-11-03 13:13:16 24 4
gpt4 key购买 nike

我有一个类如下:

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()

最佳答案

不,类上没有钩子(Hook)可以做到这一点。方法也是属性,尽管有些特殊,因为它们是在访问实例上的函数对象时产生的;功能是descriptors .

对方法对象的调用是与生成方法对象分开的步骤:

>>> 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__() method的任务调用描述符协议(protocol),因此您可以 Hook 它以查看何时生成方法,但您仍然需要包装生成的方法对象以检测调用。您可以返回一个带有 __call__ method 的对象例如,实际方法的代理。

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

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

您仍然需要将此应用到您类(class)中的所有函数。您可以手动将此应用到您想要计算的所有方法:

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 属性,因此您的类不必:

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 对象),稍后添加到类中的函数也是如此

关于Python 类方法在调用另一个方法时运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37832228/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com