【问题标题】:Python functools partial efficiencyPython functools 部分效率
【发布时间】:2013-06-27 15:04:02
【问题描述】:

我一直在使用 Python,我设置了以下代码情况:

import timeit

setting = """
import functools

def f(a,b,c):
    pass

g = functools.partial(f,c=3)    
h = functools.partial(f,b=5,c=3)   
i = functools.partial(f,a=4,b=5,c=3)
"""

print timeit.timeit('f(4,5,3)', setup = setting, number=100000)
print timeit.timeit('g(4,5)', setup = setting, number=100000)
print timeit.timeit('h(4)', setup = setting, number=100000)
print timeit.timeit('i()', setup = setting, number=100000)

我得到以下结果:

f: 0.181384086609
g: 0.39066195488
h: 0.425783157349
i: 0.391901016235

为什么调用部分函数需要更长的时间?部分函数只是将参数转发给原始函数还是将静态参数映射到整个过程?另外,在 Python 中是否有一个函数可以返回填充的函数体,因为所有参数都是预定义的,就像函数 i 一样?

【问题讨论】:

    标签: python function partial functools


    【解决方案1】:

    为什么调用部分函数需要更长的时间?

    由于额外的函数调用,partial 的代码需要大约两倍的时间。函数调用are expensive:

    Python 中的函数调用开销相对较高,尤其是与内置函数的执行速度相比。

    -

    部分函数只是将参数转发给原始函数还是映射整个静态参数?

    据我所知 - 是的,它只是 forwards the arguments to the original function

    -

    另外,如果所有参数都是预定义的,那么 Python 中是否有一个函数可以返回填充的函数的主体,就像函数 i 一样?

    不,我不知道 Python 中有这样的内置函数。但我认为可以做你想做的事,因为函数是可以复制和修改的对象。

    这是一个原型:

    import timeit
    import types
    
    
    # http://stackoverflow.com/questions/6527633/how-can-i-make-a-deepcopy-of-a-function-in-python
    def copy_func(f, name=None):
        return types.FunctionType(f.func_code, f.func_globals, name or f.func_name,
            f.func_defaults, f.func_closure)
    
    
    def f(a, b, c):
        return a + b + c
    
    
    i = copy_func(f, 'i')
    i.func_defaults = (4, 5, 3)
    
    
    print timeit.timeit('f(4,5,3)', setup = 'from __main__ import f', number=100000)
    print timeit.timeit('i()', setup = 'from __main__ import i', number=100000)
    

    给出:

    0.0257439613342
    0.0221881866455
    

    【讨论】:

    • 感谢您的帮助。你或任何人有没有机会给我一个关于如何做到这一点的提示?会涉及到操作字节码对象(function.__code__)还是只通过函数对象来完成?
    • 我已经更新了我的答案。但是你真的关心速度吗?你有什么问题?
    • 你真是太棒了。谢谢你。为了利益,我正在开发一个 Python API,并希望开发人员有一种方法可以在一个元素上定义一个函数,然后将该函数插入到一个快速循环中,但是我正在使用的 Python 实现中的函数调用相当速度令人望而却步(从我的 timeit 结果可以看出。
    【解决方案2】:

    使用部分应用的参数调用函数会更昂贵,因为函数调用的次数会增加一倍。 functools.partial()的效果和这个例子差不多:

    def apply_one_of_two(f, a):
        def g(b):
            return f(a, b)
        return g
    

    这意味着apply_one_of_two() 返回一个函数,当它被调用时,这会导致对原始函数f 的额外调用。

    由于 Python 通常不会对此进行优化,因此它会直接转化为额外的运行时工作。

    但这并不是您在微基准测试中要考虑的唯一因素。您还可以在部分调用中从位置参数切换到关键字参数,这会带来额外的开销。

    当您在原始函数中反转参数顺序时,您在部分调用中不需要关键字参数,然后运行时差异会有所减少,例如:

    import timeit
    
    setting = """
    import functools
    
    def f(a,b,c):
        pass
    
    g = functools.partial(f, 4)
    h = functools.partial(f, 4, 5)
    i = functools.partial(f, 4, 5, 3)
    """
    
    print(timeit.timeit('f(4, 5, 3)', setup = setting, number=100000))
    print(timeit.timeit('g(5, 3)',    setup = setting, number=100000))
    print(timeit.timeit('h(3)',       setup = setting, number=100000))
    print(timeit.timeit('i()',        setup = setting, number=100000))
    

    输出(在 Fedora 27/Python 3.6 下的 Intel Skylake i7 上):

    0.010069019044749439
    0.01681053702486679
    0.018060395028442144
    0.011366961000021547
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 2013-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多