Obtener "nombre global 'foo' no está definido" con el tiempo de Python

92

Estoy tratando de averiguar cuánto tiempo lleva ejecutar una declaración de Python, así que busqué en línea y descubrí que la biblioteca estándar proporciona un módulo llamado timeit que pretende hacer exactamente eso:

import timeit

def foo():
    # ... contains code I want to time ...

def dotime():
    t = timeit.Timer("foo()")
    time = t.timeit(1)
    print "took %fs\n" % (time,)

dotime()

Sin embargo, esto produce un error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in dotime
  File "/usr/local/lib/python2.6/timeit.py", line 193, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
NameError: global name 'foo' is not defined

Todavía soy nuevo en Python y no entiendo completamente todos los problemas de alcance que tiene, pero no sé por qué este fragmento no funciona. ¿Alguna idea?

Kyle Cronin
fuente

Respuestas:

93

Cambie esta línea:

t = timeit.Timer("foo()")

A esto:

t = timeit.Timer("foo()", "from __main__ import foo")

Consulte el enlace que proporcionó en la parte inferior.

Para que el módulo timeit acceda a las funciones que defina, puede pasar un parámetro de configuración que contiene una declaración de importación:

Lo probé en mi máquina y funcionó con los cambios.

Paolo Bergantino
fuente
28
¡Funciona! Sin embargo, este es un diseño de interfaz bastante estúpido si tengo que suministrar el comando que deseo cronometrar como una cadena e importar el módulo principal para que funcione.
Kyle Cronin
2
El espacio de nombres de Python es una locura para mí. Supongo que tiene sentido para cierto tipo de mente, pero ese tipo de mente no es una que yo posea. Gracias a $ DEITY por Ruby, en mi caso.
womble
5
womble, esto es una verruga, no un problema general de espacio de nombres de Python. Hilo principal: writeonly.wordpress.com/2008/09/12/… tiene enlaces a otras discusiones sobre esto.
Gregg Lind
1
@Gregg Ya no se puede acceder al enlace (error 404). ¿Qué hubo en esa discusión?
ovgolovin
2
esos enlaces están todos muertos
endolito
25

Con Python 3, puede usar globals=globals()

t = timeit.Timer("foo()", globals=globals())

De la documentación :

Otra opción es pasar globals()al globalsparámetro, lo que hará que el código se ejecute dentro de su espacio de nombres global actual. Esto puede ser más conveniente que especificar importaciones individualmente

usuario2314737
fuente
3
Funciona solo para Python 3. globalsno es un parámetro para Python 2timeit
tony_tiger
21

Puedes probar este truco:

import timeit

def foo():
    print 'bar'

def dotime():
    t = timeit.Timer("foo()")
    time = t.timeit(1)
    print "took %fs\n" % (time,)

import __builtin__
__builtin__.__dict__.update(locals())

dotime()
Luka Zakrajšek
fuente
1
Este truco es excelente si de lo contrario necesitaría un código de configuración complejo.
Luís Marques
Mejor que la alternativa de código de inicio dada en otras respuestas (es decir, mejor que t = timeit.Timer("foo()", "from __main__ import foo")). Especialmente si desea probar varias funciones diferentes, ¡le ahorrará mucho escribir!
A.Sommerh
1
Tengo alrededor de 20 importaciones, por lo que pasarlas como una discusión se vuelve complicado rápidamente. ¡Este truco es increíble!
kramer65
7
¡Excelente! En python3, sin embargo, necesita import builtinsy 'builtins .__ dict __. Update (locals ())'
greole
¿Puede cronometrar varias funciones en la función Time ()?
edo101
8
t = timeit.Timer("foo()", "from __main__ import foo")

Dado que el tiempo no tiene sus cosas en el ámbito.

dwc
fuente
0

agregue en su configuración "importar este archivo";

luego, cuando llame a la función de configuración myfunc () use "thisfile.myfunc ()"

por ejemplo, "thisfile.py"

def myfunc():

 return 5

def testable(par):

 pass



t=timeit.timeit(stmt="testable(v)",setup="import thisfile; v=thisfile.myfunc();").repeat(10)

print( t )
mist42nz
fuente