简体   繁体   English

需要更多解释如何实现这个 function 计数器

[英]Need more explanation of how to implement this function counter

From The number of times a function gets called .function 被调用的次数

Let's say, this function is called from outside the file, and I don't have access to it.比方说,这个 function 是从文件外部调用的,我无权访问它。 I am building the function, and I just want to be able to count how many times it has been called from some other part of the program.我正在构建 function,我只想计算从程序的其他部分调用它的次数。

I can't use the actual code that calls the function.我不能使用调用 function 的实际代码。 I do not have access to it.我无权访问它。 I know normally I should, but for testing purposes, I am not supposed to alter the code that calls the function.我通常知道我应该这样做,但出于测试目的,我不应该更改调用 function 的代码。 So I need to count how many times it is being called without accessing the program that calls the function.所以我需要计算它被调用了多少次而不访问调用 function 的程序。

I have tried copy and paste into the file with the function being used like so...我尝试使用 function 复制并粘贴到文件中,就像这样使用...

def counted_calls(f):
    @functools.wraps(f)
    def count_wrapper(*args, **kwargs):
        count_wrapper.count += 1
        return f(*args, **kwargs)
    count_wrapper.count = 0
    return count_wrapper

def myfunction(a, b, c, d)
    wrapped = counted_calls(counted_calls)
    integrate.quad(wrapped, 0, 1)
    print(wrapped)
    ****more of my code *****

    return something

First I get this error: Unresolved reference 'f' .首先我得到这个错误: Unresolved reference 'f'

But I get this warning:但我收到这个警告:

Expected type 'Union[function, LowLevelCallable]', got '(args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Any' instead

Then when I run it, of course, f is not defined.然后当我运行它时,当然f没有定义。

How do I get this to work so I can count how many times myfunction() is called?如何让它工作,以便我可以计算myfunction()被调用的次数?

I can't call my function, another part of the program calls this function whenever it wants to.我不能调用我的 function,程序的另一部分随时调用这个 function。 I just need to track how many times it does.我只需要跟踪它做了多少次。 I can't go put this in that part of the program and make it call the function this way.我不能 go 把它放在程序的那一部分,并让它以这种方式调用 function。 I don't have access to it.我无权访问它。
so like this?像这样?

def counted_calls(f):
    @functools.wraps(f)
    def count_wrapper(*args, **kwargs):
        count_wrapper.count += 1
        return f(*args, **kwargs)
    count_wrapper.count = 0
    return count_wrapper

wrapped = counted_calls(myfunction)
integrate.quad(wrapped, 0, 1)
print(wrapped)


def myfunction(a, b, c, d)
    code code
return something

This returns the error:这将返回错误:

TypeError: myfunction() missing 3 required positional arguments: 
'b', 'c', and 'd'

I tried this, but still wants more arguments or something?我试过了,但还是想要更多 arguments 什么的?

import functools


def counted_calls(f):
    @functools.wraps(f)
    def count_wrapper(*args, **kwargs):
        count_wrapper.count += 1
        return f(*args, **kwargs)
    count_wrapper.count = 0
    return count_wrapper


@counted_calls  # <-- Apply decorator.
def myfunction(a, b, c, d):
    ...
    return something

integrate.quad(myfunction, 0, 1)
print(myfunction.count)
TypeError: myfunction() missing 3 required positional arguments: b, c, d

So I changed it to what was recommended, however, I'm assuming b , c , and d should be something specific, I just don't know what.因此,我将其更改为推荐的内容,但是,我假设bcd应该是特定的,我只是不知道是什么。 I tried as recommended:我按照建议尝试了:

import functools


def counted_calls(f):
    @functools.wraps(f)
    def count_wrapper(*args, **kwargs):
        count_wrapper.count += 1
        return f(*args, **kwargs)
    count_wrapper.count = 0
    return count_wrapper


@counted_calls  # <-- Apply decorator.
def myfunction(a, b, c, d):
    ...
    return something

integrate.quad(myfunction, 0, 1, 2, 3) # these need to be variables of some 
                                       # kind? Not sure what to put in this 
                                       # line to make it work
print(myfunction.count)

Still missing 2 required positional arguments: 'c' and 'd'.仍然缺少 2 个必需的位置 arguments:“c”和“d”。

I hover over the 2 and it says this:我 hover 在 2 上,它说:

Expected type 'Union[Iterable, tuple, None]', got 'int' instead 

You need to apply the decorator to the function whose calls you want to count by prefixing its name with a @ and putting it on the line before the function to be decorated definition.您需要将装饰器应用到 function 的调用,方法是在其名称前加上@前缀并将其放在要装饰定义的 function 之前的行中。 Afterwards you can use the function's original name to refer to the wrapped version because using the decorator that way is just "syntactic sugar" (see glossary ) for:之后,您可以使用函数的原始名称来引用包装版本,因为以这种方式使用装饰器只是“语法糖”(参见词汇表):

myfunction = counted_calls(myfunction)

Fixed code:固定代码:

import functools


def counted_calls(f):
    @functools.wraps(f)
    def count_wrapper(*args, **kwargs):
        count_wrapper.count += 1
        return f(*args, **kwargs)
    count_wrapper.count = 0
    return count_wrapper


@counted_calls  # <-- Apply decorator.
def myfunction(a, b, c, d):
    ...
    return something

integrate.quad(myfunction, 0, 1)
print(myfunction.count)

You should be wrapping myfunction .你应该包装myfunction You do this outside the function, not inside the function you want to wrap.您在 function 外部执行此操作,而不是在您要包装的 function 内部执行此操作。

def myfunction(a, b, c, d)
    # ****more of my code *****

    return something

myfunction = counted_calls(myfunction)
# call the other part of your code that uses myfunction
print(myfunction.count)

You can define the decorator as a class that wraps the function.您可以将装饰器定义为包装 function 的 class。 By implementing the __call__ method of the class, its instance can be called like functions which allows the decorator to simply return the instance as the replacement for the declared function:通过实现 class 的__call__方法,它的实例可以像函数一样被调用,允许装饰器简单地返回实例作为声明的 function 的替换:

class CallCounter:
    def __init__(self,func):
        self.func  = func
        self.count = 0
        
    def __call__(self,*args,**kwargs):
        self.count += 1
        return self.func(*args,**kwargs)

usage:用法:

@CallCounter
def myFunction(a,b,c):
    print(a,b,c)

@CallCounter
def myOtherFunction(x):
    return x*5

for i in range(3):
    myFunction(*map(myOtherFunction,(i,i+i,i*i)))
"""
0 0 0
5 10 5
10 20 20
"""

print(myFunction.count)      # 3
print(myOtherFunction.count) # 9

In order to make it work with classes, there would be two methods to add to the CallCounter class:为了使其与类一起工作,将有两种方法添加到 CallCounter class:

    def methodCaller(self,obj):
        def withObject(*args,**kwargs):       
            return self(obj,*args,**kwargs)  # inject object instance
        return withObject

    def __get__(self,obj,objtype=None):   # return method call or CallCounter
        return self.methodCaller(obj) if obj else self 

Within a class, the CallCounter behaves like a property object which is accessed through the __get__ method.在 class 中,CallCounter 的行为类似于通过__get__方法访问的属性 object。 The __get__ method can apply to the class or to a specific object instance (obj parameter). __get__方法可以应用于 class 或特定的 object 实例(obj 参数)。 When an instance is provided in obj, the CallCounter must inject the object in the method call otherwise there will be a missing parameter.当在 obj 中提供实例时,CallCounter 必须在方法调用中注入 object 否则将缺少参数。 This is why it returns a methodCaller function that captures and inserts the obj parameter.这就是为什么它返回一个 methodCaller function 捕获并插入 obj 参数的原因。 When __get__ pertains to the class itself, then the ClassCounter object is returned directly (which allows access to its count).__get__属于 class 本身时,则直接返回 ClassCounter object(允许访问其计数)。

usage:用法:

class MyClass:

    @CallCounter
    def method1(self,s):
        print("method1",s)

x = MyClass()
for i in range(3): x.method1(i**3)

print(MyClass.method1.count) # 3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM