What do backticks mean to the Python interpreter? Example: `num`
Question:
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).
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.
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.
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.
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).
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.
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.
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.