Obter “nome global 'foo' não está definido” com o timeit do Python

90

Estou tentando descobrir quanto tempo leva para executar uma instrução Python, então olhei online e descobri que a biblioteca padrão fornece um módulo chamado timeit que pretende fazer exatamente isso:

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()

No entanto, isso produz um erro:

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

Ainda sou novo no Python e não entendo totalmente todos os problemas de escopo que ele tem, mas não sei por que esse snippet não funciona. Alguma ideia?

Kyle Cronin
fonte

Respostas:

93

Mude esta linha:

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

Para isso:

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

Confira o link que você forneceu na parte inferior.

Para dar ao módulo timeit acesso às funções definidas, você pode passar um parâmetro de configuração que contém uma instrução de importação:

Acabei de testar na minha máquina e funcionou com as alterações.

Paolo Bergantino
fonte
28
Funciona! No entanto, este é um design de interface bastante estúpido se eu tiver que fornecer o comando que desejo cronometrar como uma string e importar o módulo principal para que ele funcione.
Kyle Cronin de
2
O namespacing em Python é uma loucura absoluta para mim. Presumo que faça sentido para um certo tipo de mente, mas não é esse tipo de mente que possuo. Obrigado $ DEITY por Ruby, no meu caso.
womble em
5
womble, isso é uma verruga, não um problema geral de namespace do python. Tópico principal: writeonly.wordpress.com/2008/09/12/… tem links para outras discussões sobre isso.
Gregg Lind
1
@Gregg O link não está mais acessível (erro 404). O que houve nessa discussão?
ovgolovin
2
esses links estão todos mortos
endólito de
25

Com Python 3, você pode usar globals=globals()

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

Da documentação :

Outra opção é passar globals()para o globalsparâmetro, o que fará com que o código seja executado dentro do seu namespace global atual. Isso pode ser mais conveniente do que especificar importações individualmente

user2314737
fonte
3
Funciona apenas para Python 3. globalsnão é um parâmetro para Python 2timeit
tony_tiger
21

Você pode tentar este hack:

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
fonte
1
Este hack é ótimo se você precisar de um código de configuração complexo.
Luís Marques
Melhor do que a alternativa de código de inicialização fornecida em outras respostas (ou seja, melhor do que t = timeit.Timer("foo()", "from __main__ import foo")). Especialmente se você quiser testar várias funções diferentes, vai economizar muita digitação!
A.Sommerh
1
Tenho cerca de 20 importações, portanto, passá-los como um argumento fica confuso rapidamente. Este hack é incrível!
Kramer65
7
Ótimo! No python3, entretanto, você precisa de import builtins'builtins .__ dict __. Update (locals ())'
greole
Você pode cronometrar várias funções na função Time ()?
edo101
8
t = timeit.Timer("foo()", "from __main__ import foo")

Desde o tempo, não tem seu material em escopo.

dwc
fonte
0

adicione em sua configuração "import thisfile;"

então, quando você chamar a função de configuração myfunc (), use "thisfile.myfunc ()"

por exemplo, "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
fonte