我有一个C ++库,该库通过python包装和公开。由于各种原因,在通过python公开函数时,我需要重载函数__call__
。
下面是一个最小的示例,使用time.sleep
来模拟具有各种计算时间的函数
import sys
import time
class Wrap_Func(object):
def __init__(self, func, name):
self.name = name
self.func = func
def __call__(self, *args, **kwargs):
# do stuff
return self.func(*args, **kwargs)
def wrap_funcs():
thismodule = sys.modules[__name__]
for i in range(3):
fname = 'example{}'.format(i)
setattr(thismodule, fname, Wrap_Func(lambda: time.sleep(i), fname))
wrap_funcs()
通过cProfile
对代码进行性能分析时,我会得到__call__
例程的列表。
我无法确定哪些例程占用了大部分计算时间。
>>> import cProfile
>>> cProfile.runctx('example0(); example1(); example2()', globals(), locals())
11 function calls in 6.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
3 0.000 0.000 6.000 2.000 <ipython-input-48-e8126c5f6ea3>:11(__call__)
3 0.000 0.000 6.000 2.000 <ipython-input-48-e8126c5f6ea3>:20(<lambda>)
1 0.000 0.000 6.000 6.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
3 6.000 2.000 6.000 2.000 {time.sleep}
通过手动将函数定义为(
def example0():
time.sleep(0)
def example1():
time.sleep(1)
def example2():
time.sleep(2)
我得到了预期的输出
>>> import cProfile
>>> cProfile.runctx('example0(); example1(); example2()', globals(), locals())
11 function calls in 6.000 seconds
8 function calls in 3.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <ipython-input-58-688d247cb941>:1(example0)
1 0.000 0.000 0.999 0.999 <ipython-input-58-688d247cb941>:4(example1)
1 0.000 0.000 2.000 2.000 <ipython-input-58-688d247cb941>:7(example2)
1 0.000 0.000 3.000 3.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
3 3.000 1.000 3.000 1.000 {time.sleep}
答案 0 :(得分:1)
以下内容结合了@ alex-martelli到resolve的补丁,修补了特殊的__call__
方法,以及@ martijn-pieters到solve来正确重命名功能代码对象的问题
import types
def rename_code_object(func, new_name):
code = func.__code__
return types.FunctionType(
types.CodeType(
code.co_argcount, code.co_nlocals,
code.co_stacksize, code.co_flags,
code.co_code, code.co_consts,
code.co_names, code.co_varnames,
code.co_filename, new_name,
code.co_firstlineno, code.co_lnotab,
code.co_freevars, code.co_cellvars),
func.__globals__, new_name, func.__defaults__, func.__closure__)
class ProperlyWrapFunc(Wrap_Func):
def __init__(self, func, name):
super(ProperlyWrapFunc, self).__init__(func, name)
renamed = rename_code_object(super(ProperlyWrapFunc, self).__call__, name)
self.__class__ = type(name, (ProperlyWrapFunc,), {'__call__': renamed})
在调用使用新类的修改wrap_funcs()
之后,我们得到了预期的输出。这也应该与异常回溯兼容
>>> import cProfile
>>> cProfile.runctx('example0(); example1(); example2()', globals(), locals())
11 function calls in 6.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 2.000 2.000 <ipython-input-1-96920f80be1c>:9(example0)
1 0.000 0.000 2.000 2.000 <ipython-input-1-96920f80be1c>:9(example1)
1 0.000 0.000 2.000 2.000 <ipython-input-1-96920f80be1c>:9(example2)
3 0.000 0.000 6.000 2.000 <ipython-input-9-ed938f395cb4>:30(<lambda>)
1 0.000 0.000 6.000 6.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
3 6.000 2.000 6.000 2.000 {time.sleep}