Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - curious/unexpected behaviour - precedence of operators

I have recently been experimenting with python generators a bit, and I came across the following curious behaviour, and I am curious to understand why this happens and what is going on:

def generating_test(n): 
    for a in range(n): 
        yield "a squared is %s" % a*a # Notice instead of a**2 we have written a*a

for asquare in generating_test(3): 
    print asquare 

Output:

a squared is 1
a squared is 2a squared is 2

Versus the following script which generates the expected output:

def generating_test(n): 
    for a in range(n): 
        yield "a squared is %s" % a**2 # we use the correct a**2 here

for asquare in generating_test(3): 
    print asquare 

Output:

a squared is 0
a squared is 1
a squared is 4
like image 332
Alexander Marquardt Avatar asked Aug 07 '12 14:08

Alexander Marquardt


People also ask

What is the order of precedence of operators in Python?

Python will always evaluate the arithmetic operators first (** is highest, then multiplication/division, then addition/subtraction). Next comes the relational operators. Finally, the logical operators are done last.

Which Python operator have higher precedence?

Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you want.

Which operator has higher precedence in the following list?

Which among the following list of operators has the highest precedence? Explanation: The highest precedence is that of the exponentiation operator, that is of **.


3 Answers

This doesn't have anything to do with generators:

>>> a = 2
>>> "a squared is %s" % a
'a squared is 2'
>>> ("a squared is %s" % a)*a
'a squared is 2a squared is 2'
>>> "a squared is %s" % a*a
'a squared is 2a squared is 2'
>>> "a squared is %s" % (a*a)
'a squared is 4'

The % op is performed before the multiplication, using the string and the first a as arguments. Your a**2 works because the ** op with a and 2 as arguments is evaluated before the %.

like image 90
DSM Avatar answered Nov 15 '22 23:11

DSM


Python's order of operations is from left-to-right except where PEMDAS applies. The string interpolation operator apparently has the same precedence as modulo and multiplication, because if you reverse the order, making the multiplication left of the interpolation, it takes precedence:

>>> print 3 * "a foo %s" % 'hi'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
>>> print 3 * "a foo %s" % ('hi', 'ho', 'yo')
a foo hia foo hoa foo yo

However, as you've demonstrated, exponentiation trumps left-to-right order.

Update: In that same document under Binary Arithmetic Operations it states something denotatively obvious, but connotatively relevant:

…the % operator is also overloaded by string and unicode objects to perform string formatting (also known as interpolation).

While that appears to just be telling you what the % operator does, I think its location and context also tells you it has the same precedence whether it's used as modulo or interpolate.

like image 24
kojiro Avatar answered Nov 15 '22 23:11

kojiro


When you observe unexpected behaviour, start your analysis by distilling it to its simplest possible case. A simple case will easier to study and comprehend.

The unexpected behaviour:

>>> 'hello %s' % 3 * 2
'hello 3hello 3'

(You expected 'hello 6')


We reason Python must be interpreting the command as 'hello 3' * 2 rather than 'hello %d' % 6. We try forcing the second interpretation with brackets

>>> "hello %s" % (3*2)
'hello 6'

Eureka!

We've demonstrated that the string formatting operator % has greater or equal precedence than multiplication. We check the Python documentation - yes it confirms this http://docs.python.org/reference/expressions.html#summary

To confirm that the precedence is equal, we can try it the other way around:

>>> "%d,"*2%(1,2)
'1,2,'

Seeing that the comma (,) is duplicated, we reason that the multiplication "%d," * 2 was performed before the string formatting %. If multiplication can precede string formatting, and string formatting precede multiplication, they must be equal in precedence.

like image 44
Colonel Panic Avatar answered Nov 16 '22 00:11

Colonel Panic