Cosa significano i backtick per l'interprete Python: `num`


88

Sto giocando con la comprensione degli elenchi e mi sono imbattuto in questo piccolo frammento su un altro sito:

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

Ho passato alcuni minuti a provare a replicare la funzione (digitando) prima di rendermi conto che il `num`bit la stava interrompendo.

Cosa fa racchiudere una dichiarazione in quei caratteri? Da quello che posso vedere è l'equivalente di str (num). Ma quando l'ho cronometrato:

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

Ci vogliono 4.09s mentre:

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

impiega 2,43 secondi.

Entrambi danno risultati identici ma uno è molto più lento. Che cosa sta succedendo qui?

EDIT: Stranamente ... repr()dà risultati leggermente più lenti di `num`. 2,99 contro 2,43. Utilizzo di Python 2.6 (non ho ancora provato 3.0).


8
Dopo aver letto "un altro sito" su skymind.com/~ocrow/python_string , ho avuto una domanda simile e ho trovato questa pagina. Bella domanda e bella risposta :)
netvope

Risposte:


123

I backtick sono un alias deprecato per repr(). Non usarli più, la sintassi è stata rimossa in Python 3.0.

L'uso dei backtick sembra essere più veloce rispetto all'utilizzo repr(num)o num.__repr__()nella versione 2.x. Immagino sia perché è necessaria una ricerca aggiuntiva nel dizionario nello spazio dei nomi globale (per repr) o nello spazio dei nomi dell'oggetto (per __repr__), rispettivamente.


L'utilizzo del dismodulo dimostra la mia ipotesi:

def f1(a):
    return repr(a)

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

def f3(a):
    return `a`

Spettacoli di smontaggio:

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

f1implica una ricerca globale di repr, f2una ricerca di attributi __repr__, mentre l'operatore di backtick è implementato in un codice operativo separato. Poiché non vi è alcun sovraccarico per la ricerca nel dizionario ( LOAD_GLOBAL/ LOAD_ATTR) né per le chiamate di funzione ( CALL_FUNCTION), i backtick sono più veloci.

Immagino che la gente di Python abbia deciso che repr()non vale la pena avere un'operazione separata di basso livello per , e avere entrambi repr()e backtick viola il principio

"Ci dovrebbe essere uno - e preferibilmente solo uno - modo ovvio per farlo"

quindi la funzionalità è stata rimossa in Python 3.0.


Volevo scoprire come sostituire i backtick con qualche chiamata di funzione, ma sembra che non sia possibile, o no?
Jiri

2
Usa repr () invece di backtick. I backtick sono una sintassi deprezzata per repr () come 3.0. In realtà preferisco l'aspetto dei backtick piuttosto che chiamare UN'ALTRA funzione.
Dominic Bou-Samra

8
Il motivo per cui i backtick sono deprecati è anche a causa del carattere stesso; può essere difficile da digitare (su alcune tastiere), difficile da vedere di cosa si tratta, difficile da stampare correttamente nei libri Python. Etc.
u0b34a0f6ae

4
@ kaizer.se: Grazie per averlo fatto notare. Questo è probabilmente il motivo principale per eliminare i backtick, vedere la dichiarazione di Guidos negli archivi della mailing list: mail.python.org/pipermail/python-ideas/2007-January/000054.html
Ferdinand Beyer

La domanda originale che è stata posta era perché non riuscivo a trovare i backtick sulla mia tastiera;) Sotto la tilde sembra dopo aver cercato su Google.
Dominic Bou-Samra

10

La citazione di backtick è generalmente non utile e scomparsa in Python 3.

Per quello che vale, questo:

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

è leggermente più veloce della versione backtick per me. Ma preoccuparsi di questo è probabilmente un'ottimizzazione prematura.


2
Perché fare un passo indietro e usare la mappa invece delle comprensioni di elenchi / iteratori?
nikow

4
In realtà, timeitproduce risultati più rapidi per ''.join(map(repr, xrange(0, 1000000)))che per ''.join([repr(i) for i in xrange(0, 1000000)])(anche peggio per ''.join( (repr(i) for i in xrange(0, 1000000)) )). È un po 'deludente ;-)
RedGlyph

8
Il risultato di Bobince non mi sorprende. Come regola generale, i cicli impliciti in Python sono più veloci di quelli espliciti, spesso notevolmente più veloci. mapè implementato in C, utilizzando un ciclo C, che è molto più veloce di un ciclo Python eseguito nella macchina virtuale.
Ferdinand Beyer

7
Nemmeno sorpreso, è solo un peccato per la reputazione delle liste di comprensione (con un 30% di successo in questo esempio). Ma preferisco avere un codice chiaro piuttosto che velocissimo a meno che non sia davvero importante, quindi non è un grosso problema qui. Detto questo, la funzione map () non mi sembra poco chiara, le LC a volte sono sopravvalutate.
RedGlyph

4
mapmi sembra perfettamente chiaro e conciso, e non conosco nemmeno Python.
Zenexer

1

La mia ipotesi è che numnon definisce il metodo __str__(), quindi str()deve fare una seconda ricerca per __repr__.

I backtick cercano direttamente __repr__. Se questo è vero, l'uso al repr()posto dei backtick dovrebbe darti gli stessi risultati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.