En Python 3.9, las funciones anidadas son sorprendentemente más lentas que las funciones normales, alrededor del 10 % en mi ejemplo.
from timeit import timeit def f(): return 0 def factory(): def g(): return 0 return g g = factory() print(timeit("f()", globals=globals())) #> 0.074835498 print(timeit("g()", globals=globals())) #> 0.08470309999999998 dis.dis muestra el mismo código de bytes, y la única diferencia que encontré fue en los indicadores internos de función. De hecho, dis.show_code revela que g tiene banderas NESTED mientras que f no las tiene.
Sin embargo, las banderas se pueden eliminar y hace que g sea tan rápido como f .
import inspect g.__code__ = g.__code__.replace(co_flags=g.__code__.co_flags ^ inspect.CO_NESTED) print(timeit("f()", globals=globals())) #> 0.07321161100000001 print(timeit("g()", globals=globals())) #> 0.07439838800000001 Intenté mirar el código CPython para comprender cómo el indicador CO_NESTED podría afectar la ejecución de la función, pero no encontré nada. ¿Hay alguna explicación para esta diferencia de rendimiento en relación con el indicador CO_NESTED ?
EDITAR : Eliminar el indicador CO_NESTED tampoco parece tener impacto en la ejecución de la función, excepto la sobrecarga, incluso cuando ha capturado la variable.
import inspect global_var = 40 def factory(): captured_var = 2 def g(): return global_var + captured_var return g g = factory() assert g() == 42 g.__code__ = g.__code__.replace(co_flags=g.__code__.co_flags ^ inspect.CO_NESTED) assert g() == 42 # function still works as expectedPuede que me equivoque, pero creo que la diferencia proviene del hecho de que g puede potencialmente hacer referencia a las variables locales de factory y, como tal, necesita acceso a dos ámbitos para cualquier búsqueda de variables: globals y de factory . Bien puede ser que asegurar este alcance adicional (o fusionar el alcance de factory y globals ) sea la causa de la sobrecarga que observa. Una buena pista de que sucede es si anida otro nivel de funciones:
def factory(): def ff(): def g(): return 0 return g return ff() g = factory() # please note that it is equivalent from the perspective of time measurementHorarios:
print(timeit("f()", globals=globals(), number=100000000)) # > 6.792911 print(timeit("g()", globals=globals(), number=100000000)) # > 7.8184555En su primer caso de tiempo obtengo +5,7% (era +13,5% con sus números), en mi segundo ejemplo: +15,1%.