What do backticks mean to the Python interpreter? Example: `num`

PythonList Comprehension

Python Problem Overview


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

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

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.

Solution 3 - Python

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDominic Bou-SamraView Question on Stackoverflow
Solution 1 - PythonFerdinand BeyerView Answer on Stackoverflow
Solution 2 - PythonbobinceView Answer on Stackoverflow
Solution 3 - PythonAaron DigullaView Answer on Stackoverflow