Частичная эффективность Python functools
Я работал с 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?
Ответы
Ответ 1
Почему вызовы частичных функций занимают больше времени?
Код с partial
занимает примерно в два раза больше из-за вызова дополнительной функции. Функциональные вызовы стоят дорого:
Накладные расходы функций в Python относительно высоки, особенно по сравнению со скоростью выполнения встроенной функции.
-
Является ли частичная функция просто перенаправлением параметров исходной функции или она отображает статические аргументы?
Насколько я знаю - да, просто переводит аргументы в исходную функцию.
-
А также существует ли функция в 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
Ответ 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))
Выход (на Intel Skylake i7 в Fedora 27/Python 3.6):
0.010069019044749439
0.01681053702486679
0.018060395028442144
0.011366961000021547