Обратите внимание, что из-за ошибки, ранее обнаруженной в моем коде тестирования, мой первоначальный ответ был неверным.Ниже приведена пересмотренная версия.
Я сделал небольшую программу для измерения времени работы и потребления памяти.Я создал следующий вызываемый класс и замыкание:
class CallMe:
def __init__(self, context):
self.context = context
def __call__(self, *args, **kwargs):
return self.context(*args, **kwargs)
def call_me(func):
return lambda *args, **kwargs: func(*args, **kwargs)
Я синхронизировал вызовы простых функций, принимающих разное количество аргументов (math.sqrt()
с 1 аргументом, math.pow()
с 2 и max()
с 12),
Я использовал CPython 2.7.10 и 3.4.3+ в Linux x64.Я смог выполнить профилирование памяти только на Python 2. Использованный мной исходный код доступен здесь .
Мои выводы таковы:
- Замыкания работают быстрее, чемэквивалентные вызываемые классы: примерно в 3 раза быстрее на Python 2, но только в 1,5 раза быстрее на Python 3. Сужение происходит потому, что закрытие стало медленнее, а вызываемые классы медленнее.
- Замыкания занимают меньше памяти, чем эквивалентные вызываемые классы: примерно2/3 памяти (проверено только на Python 2).
- Хотя это и не является частью исходного вопроса, интересно отметить, что накладные расходы времени выполнения для вызовов, сделанных с помощью замыкания, примерно такие же, как вызовдо
math.pow()
, тогда как через вызываемый класс это примерно вдвое больше.
Это очень приблизительные оценки, и они могут варьироваться в зависимости от аппаратного обеспечения, операционной системы и функции, которую вы сравниваете.Тем не менее, это дает вам представление о влиянии использования каждого вида вызываемого абонента.
Таким образом, это подтверждает (наоборот, то, что я написал ранее), что принятый ответ @RaymondHettinger является правильным, иЗакрытия должны быть предпочтительнее для косвенных вызовов, по крайней мере, до тех пор, пока они не ухудшают читабельность.Также спасибо @AXO за указание на ошибку в моем исходном коде.