Was bedeuten Backticks für den Python-Interpreter: `num`

87

Ich spiele mit Listenverständnissen herum und bin auf einer anderen Seite auf diesen kleinen Ausschnitt gestoßen:

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

Ich habe ein paar Minuten damit verbracht, die Funktion zu replizieren (durch Eingabe), bevor ich merkte, dass das `num`Bit sie kaputt machte.

Was bewirkt das Einfügen einer Anweisung in diese Zeichen? Soweit ich sehen kann, entspricht es str (num). Aber als ich es zeitlich festlegte:

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

Es dauert 4,09 Sekunden, während:

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

dauert 2,43s.

Beide liefern identische Ergebnisse, aber eines ist viel langsamer. Was geht hier vor sich?

EDIT: Seltsamerweise ... repr()liefert etwas langsamere Ergebnisse als `num`. 2,99s vs 2,43s. Verwenden von Python 2.6 (habe 3.0 noch nicht ausprobiert).

Dominic Bou-Samra
quelle
8
Nachdem ich die "andere Site" unter skymind.com/~ocrow/python_string gelesen hatte , hatte ich eine ähnliche Frage und fand diese Seite. Schöne Frage und schöne Antwort :)
Netvope

Antworten:

122

Backticks sind ein veralteter Alias ​​für repr(). Verwenden Sie sie nicht mehr, die Syntax wurde in Python 3.0 entfernt.

Die Verwendung von Backticks scheint schneller zu sein als die Verwendung von repr(num)oder num.__repr__()in Version 2.x. Ich denke, das liegt daran, dass eine zusätzliche Wörterbuchsuche im globalen Namespace (für repr) bzw. im Namespace des Objekts (für __repr__) erforderlich ist .


Die Verwendung des disModuls beweist meine Annahme:

def f1(a):
    return repr(a)

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

def f3(a):
    return `a`

Demontage zeigt:

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

f1beinhaltet eine globale Suche nach repr, f2eine Attributsuche nach __repr__, während der Backtick-Operator in einem separaten Opcode implementiert ist. Da weder für die Wörterbuchsuche ( LOAD_GLOBAL/ LOAD_ATTR) noch für Funktionsaufrufe ( CALL_FUNCTION) ein Overhead anfällt, sind Backticks schneller.

Ich denke, dass die Python-Leute entschieden haben, dass es repr()sich nicht lohnt , eine separate Low-Level-Operation zu haben, und dass beides repr()und Backticks gegen das Prinzip verstoßen

"Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun."

Daher wurde die Funktion in Python 3.0 entfernt.

Ferdinand Beyer
quelle
Ich wollte herausfinden, wie man Backticks durch einen Funktionsaufruf ersetzen kann, aber es scheint, dass dies nicht möglich ist, oder?
Jiri
2
Verwenden Sie repr () anstelle von Backticks. Backticks sind abgeschriebene Syntax für repr () ab 3.0. Eigentlich bevorzuge ich das Aussehen von Backticks, anstatt eine andere Funktion aufzurufen.
Dominic Bou-Samra
8
Der Grund, warum Backticks veraltet sind, liegt auch am `Charakter selbst; Es kann schwierig sein (auf einigen Tastaturen) zu tippen, schwer zu erkennen, was es ist, und es ist schwierig, in Python-Büchern richtig zu drucken. Etc.
u0b34a0f6ae
4
@ kaizer.se: Danke, dass du darauf hingewiesen hast. Dies ist wahrscheinlich der Hauptgrund für das Löschen von Backticks, siehe Guidos-Erklärung im Mailinglisten-Archiv: mail.python.org/pipermail/python-ideas/2007-January/000054.html
Ferdinand Beyer
Die ursprüngliche Frage, die hier gestellt wurde, war, weil ich keine Backticks auf meiner Tastatur finden konnte;) Unterhalb der Tilde scheint es nach dem Googeln.
Dominic Bou-Samra
10

Backtick-Zitate sind im Allgemeinen nicht nützlich und in Python 3 nicht mehr verfügbar.

Für das, was es wert ist, dies:

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

ist für mich etwas schneller als die Backtick-Version. Aber sich darüber Sorgen zu machen, ist wahrscheinlich eine vorzeitige Optimierung.

Bobince
quelle
2
Warum einen Schritt zurückgehen und Map anstelle von Listen- / Iterator-Verständnis verwenden?
Nikow
4
Tatsächlich timeitergeben sich schnellere Ergebnisse ''.join(map(repr, xrange(0, 1000000)))als für ''.join([repr(i) for i in xrange(0, 1000000)])(noch schlimmer für ''.join( (repr(i) for i in xrange(0, 1000000)) )). Es ist ein bisschen enttäuschend ;-)
RedGlyph
8
Das Ergebnis von Bobince überrascht mich nicht. Als Faustregel gilt, dass implizite Schleifen in Python schneller sind als explizite, oftmals dramatisch schneller. mapwird in C mithilfe einer C-Schleife implementiert, die viel schneller ist als eine in der virtuellen Maschine ausgeführte Python-Schleife.
Ferdinand Beyer
7
Kein Wunder, es ist einfach zu schlecht für den Ruf des Listenverständnisses (mit einem Treffer von 30% in diesem Beispiel). Aber ich hätte lieber einen klaren als blitzschnellen Code, es sei denn, dies ist wirklich wichtig, also keine große Sache hier. Davon abgesehen erscheint mir die Funktion map () nicht unklar, LC werden manchmal überbewertet.
RedGlyph
4
mapscheint mir vollkommen klar und prägnant zu sein, und ich kenne Python nicht einmal.
Zenexer
1

Ich vermute, dass numdie Methode nicht definiert ist __str__(), also str()muss eine zweite Suche durchgeführt werden __repr__.

Die Backticks suchen direkt nach __repr__. Wenn dies zutrifft, repr()sollten Sie anstelle der Backticks dieselben Ergebnisse erzielen.

Aaron Digulla
quelle