Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What do backticks mean to the python interpreter: `num`

I'm playing around with list comprehensions and I came across this little snippet on another site:

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

I spent a few minutes trying to replicate the function (by typing) before realising the `num` bit was breaking it.

What does enclosing a statement in those characters do? From what I can see it is the equivalent of str(num). But when I timed it:

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

It takes 4.09 seconds whereas:

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

takes 2.43 seconds.

Both give identical results, but one is a lot slower. What is going on here?

Oddly... repr() gives slightly slower results than `num`. 2.99 seconds vs 2.43 seconds. I am using Python 2.6 (haven't tried 3.0 yet).

like image 625
Dominic Bou-Samra Avatar asked Nov 04 '09 11:11

Dominic Bou-Samra


3 Answers

Backticks are a deprecated alias for repr(). Don't use them any more; the syntax was removed in Python 3.0.

Using backticks seems to be faster than using repr(num) or num.__repr__() in version 2.x. I guess it's because additional dictionary lookup is required in the global namespace (for repr), or in the object's namespace (for __repr__), respectively.


Using the dis module proves my assumption:

def f1(a):
    return repr(a)

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

def f3(a):
    return `a`

Disassembling shows:

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

f1 involves a global lookup for repr, f2 an attribute lookup for __repr__, whereas the backtick operator is implemented in a separate opcode. Since there is no overhead for dictionary lookup (LOAD_GLOBAL/LOAD_ATTR) nor for function calls (CALL_FUNCTION), backticks are faster.

I guess that the Python folks decided that having a separate low-level operation for repr() is not worth it, and having both repr() and backticks violates the principle

"There should be one-- and preferably only one --obvious way to do it"

so the feature was removed in Python 3.0.

like image 56
Ferdinand Beyer Avatar answered Oct 23 '22 21:10

Ferdinand Beyer


Backtick quoting is generally non-useful and is gone in Python 3.

For what it's worth, this:

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

is marginally faster than the backtick version for me. But worrying about this is probably a premature optimisation.

like image 10
bobince Avatar answered Oct 23 '22 20:10

bobince


My guess is that num doesn't define the method __str__(), so str() has to do a second lookup for __repr__.

The backticks look directly for __repr__. If that's true, then using repr() instead of the backticks should give you the same results.

like image 1
Aaron Digulla Avatar answered Oct 23 '22 21:10

Aaron Digulla