views:

409

answers:

3

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.09s whereas:

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

takes 2.43s.

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

EDIT: Oddly... repr() gives slightly slower results then num. 2.99s vs 2.43s. Using Python 2.6 (haven't tried 3.0 yet)

+13  A: 

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.

EDIT: 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 nor for function calls, 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.

Ferdinand Beyer
See my latest edit on repr()
Dominic Bou-Samra
I wanted to find, how you can replace backticks with some function call, but it seems that it is not possible, or is it?
Jiri
Use repr() instead of backticks. Backticks are depreciated syntax for repr() come 3.0. I actually prefer the look of backticks rather then calling ANOTHER function.
Dominic Bou-Samra
The reason backticks are deprecated is also because of the ` character itself; it can be hard to type (on some keyboards), hard to see what it is, hard to print correctly in Python books. Etc.
kaizer.se
@kaizer.se: Thanks for pointing that out. This is probably the main reason for dropping backticks, see Guidos statement in the mailing list archives: http://mail.python.org/pipermail/python-ideas/2007-January/000054.html
Ferdinand Beyer
The original question this was posted was because I couldn't actually find backticks on my keyboard ;) Below the tilde it seems after googling.
Dominic Bou-Samra
+1  A: 

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.

Aaron Digulla
+3  A: 

Backtick quoting is generally non-useful and 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.

bobince
Why go a step backwards and use map instead of list/iterator comprehensions?
nikow
Actually, `timeit` yields faster results for `''.join(map(repr, xrange(0, 1000000)))` than for `''.join([repr(i) for i in xrange(0, 1000000)])` (even worse for `''.join( (repr(i) for i in xrange(0, 1000000)) )`). It's a bit disappointing ;-)
RedGlyph
bobince's result is not surprising to me. As as rule of thumb, implicit loops in Python are faster than explicit ones, often dramatically faster. `map` is implemented in C, using a C loop, that is much faster than a Python loop executed in the virtual machine.
Ferdinand Beyer
Not surprised either, it's just too bad for the list comprehensions' reputation (with a 30% hit in this example). But I'd rather have clear than blazing-speed code unless this is really important, so no big deal here. That being said, the map() function doesn't strike me as unclear, LC are sometimes overrated.
RedGlyph