O que crases significam para o interpretador Python: `num`

87

Estou brincando com as compreensões de listas e encontrei este pequeno trecho em outro site:

return ''.join([`num` for num in xrange(loop_count)])

Passei alguns minutos tentando replicar a função (digitando) antes de perceber que o `num`bit a estava quebrando.

O que incluir uma declaração nesses caracteres? Pelo que posso ver, é o equivalente a str (num). Mas quando eu cronometrei:

return ''.join([str(num) for num in xrange(10000000)])

Leva 4,09s, enquanto:

return ''.join([`num` for num in xrange(10000000)])

leva 2,43s.

Ambos fornecem resultados idênticos, mas um é muito mais lento. O que está acontecendo aqui?

EDIT: Estranhamente ... repr()dá resultados ligeiramente mais lentos do que `num`. 2,99s vs 2,43s. Usando Python 2.6 (não tentei 3.0 ainda).

Dominic Bou-Samra
fonte
8
Depois de ler o "outro site" em skymind.com/~ocrow/python_string , tive uma pergunta semelhante e encontrei esta página. Boa pergunta e boa resposta :)
netvope

Respostas:

122

Backticks são um alias obsoleto para repr() . Não os use mais, a sintaxe foi removida no Python 3.0.

Usar crases parece ser mais rápido do que usar repr(num)ou num.__repr__()na versão 2.x. Acho que é porque a pesquisa de dicionário adicional é necessária no namespace global (for repr) ou no namespace do objeto (for __repr__), respectivamente.


Usar o dismódulo prova minha suposição:

def f1(a):
    return repr(a)

def f2(a):
    return a.__repr__()

def f3(a):
    return `a`

Desmontando programas:

>>> import dis
>>> dis.dis(f1)
  3           0 LOAD_GLOBAL              0 (repr)
              3 LOAD_FAST                0 (a)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE
>>> dis.dis(f2)
  6           0 LOAD_FAST                0 (a)
              3 LOAD_ATTR                0 (__repr__)
              6 CALL_FUNCTION            0
              9 RETURN_VALUE        
>>> dis.dis(f3)
  9           0 LOAD_FAST                0 (a)
              3 UNARY_CONVERT       
              4 RETURN_VALUE   

f1envolve uma pesquisa global para repr, f2uma pesquisa de atributo para __repr__, enquanto o operador crase é implementado em um opcode separado. Uma vez que não há sobrecarga para pesquisa de dicionário ( LOAD_GLOBAL/ LOAD_ATTR) nem para chamadas de função (CALL_FUNCTION ), crases são mais rápidos.

Acho que o pessoal do Python decidiu que repr()não vale a pena ter uma operação de baixo nível separada para , e ter ambos os repr()crases viola o princípio

"Deve haver uma - e de preferência apenas uma - maneira óbvia de fazer isso"

portanto, o recurso foi removido no Python 3.0.

Ferdinand Beyer
fonte
Eu queria descobrir como você pode substituir crases com alguma chamada de função, mas parece que não é possível, ou é?
Jiri
2
Use repr () em vez de crases. Backticks são sintaxe depreciada para repr () come 3.0. Na verdade, prefiro a aparência de crases em vez de chamar OUTRA função.
Dominic Bou-Samra
8
A razão pela qual os crases estão obsoletos também é por causa do próprio caractere `; pode ser difícil de digitar (em alguns teclados), difícil de ver o que é, difícil de imprimir corretamente em livros Python. Etc.
u0b34a0f6ae
4
@ kaizer.se: Obrigado por apontar isso. Esta é provavelmente a principal razão para abandonar os backticks, consulte a declaração de Guidos nos arquivos da lista de discussão: mail.python.org/pipermail/python-ideas/2007-January/000054.html
Ferdinand Beyer
A pergunta original que isso foi postada foi porque eu não conseguia encontrar backticks no meu teclado;) Abaixo do til parece depois de pesquisar no Google.
Dominic Bou-Samra
10

As citações de backtick geralmente não são úteis e desapareceram no Python 3.

Por que vale a pena, isto:

''.join(map(repr, xrange(10000000)))

é ligeiramente mais rápido do que a versão backtick para mim. Mas se preocupar com isso é provavelmente uma otimização prematura.

bobince
fonte
2
Por que retroceder e usar o mapa em vez de compreensões de lista / iterador?
nikow
4
Na verdade, timeitproduz resultados mais rápidos para do ''.join(map(repr, xrange(0, 1000000)))que para ''.join([repr(i) for i in xrange(0, 1000000)])(ainda pior para ''.join( (repr(i) for i in xrange(0, 1000000)) )). É um pouco decepcionante ;-)
RedGlyph
8
O resultado de bobince não me surpreendeu. Como regra geral, os loops implícitos em Python são mais rápidos do que os explícitos, geralmente muito mais rápidos. mapé implementado em C, usando um loop C, que é muito mais rápido do que um loop Python executado na máquina virtual.
Ferdinand Beyer
7
Também não é surpresa, é muito ruim para a reputação das compreensões de lista (com um acerto de 30% neste exemplo). Mas eu prefiro ter um código claro do que de alta velocidade, a menos que isso seja realmente importante, então não há grande problema aqui. Dito isso, a função map () não me parece pouco clara, os LC às vezes são superestimados.
RedGlyph
4
mapparece perfeitamente claro e conciso para mim, e eu nem mesmo conheço Python.
Zenexer
1

Meu palpite é que numisso não define o método __str__(), então str()é necessário fazer uma segunda pesquisa para __repr__.

Os acúmulos procuram diretamente __repr__. Se isso for verdade, usar em repr()vez dos crases deve dar os mesmos resultados.

Aaron Digulla
fonte