-2

Following up my previous question I have the following one. Are these the same in Python?

a += b[1] / 2

and

a += (b[1] / 2)

Providing that:

  • a has already been defined earlier as float
  • b is a list of tuples
  • b[1] is a tuple with a single element

Similarly I would be interested to learn as well the behavior if:

  • a has already been defined earlier as float
  • b is a list of floats
Manngo
  • 829
  • 7
  • 24
  • 3
    The type is irrelevant for the precedence in parsing. – Willem Van Onsem Sep 25 '18 at 19:32
  • I think what you're asking is "since `a += expr` is more-or-less equivalent to `a = a + expr`, do I need to worry about the precedence of addition compared to the precedence of the other operators in the expression?" I think the existing answer on your previous question covers that: the addition and the assignment always happen last. – Kevin Sep 25 '18 at 19:38
  • Yes, those are **still the same thing**. It doesn't matter what variations of expressions you come up with, the `+=` augmented assignment still will not be anything more than a statement. – Martijn Pieters Sep 25 '18 at 19:39
  • It's best to think of `a += expr` as being *suggestive* of `a = a + expr`; the actual semantics of an augmented assignment operator are independent of the corresponding binary operator. (That is, `a += b` doesn't *have* to mean anything like `a = a + b`, but it would be very surprising it wasn't as close as possible to it.) – chepner Sep 25 '18 at 19:40
  • And if `b[1]` really is a `tuple`, then the expression will raise an exception as there is no division operation on tuples. – Martijn Pieters Sep 25 '18 at 19:40
  • Next, it doesn't matter what the types of the objects are; precedence parsing only looks at the actual operators in an expression, not what the names resolve to. That's not something the compilation step cares about because types are not known until you run the compiled code. – Martijn Pieters Sep 25 '18 at 19:42
  • @Martijn It is a tuple created by tensorflow `sess.run` and eventually [reduce_mean](https://www.tensorflow.org/api_docs/python/tf/reduce_mean) function and it doesn't raise and exception upon division. That's why I'm asking this second question. – Manngo Sep 25 '18 at 19:48
  • OK, I see now I asked the wrong question. `b` is actually a list of tensors that for some reason shown as a list of tuples in my editor. – Manngo Sep 25 '18 at 20:00
  • @Manngo: right, that's a [different tuple](https://www.tensorflow.org/api_docs/python/tf/tuple) from the [standard Python type](https://docs.python.org/3/library/stdtypes.html#tuple), which is what we normally mean when you say 'tuple'. – Martijn Pieters Sep 25 '18 at 20:04
  • Follow up with the question I actually wanted to ask is [here](https://stackoverflow.com/questions/52505979/python-operator-precedence-with-augmented-assignment-in-tensorflow) – Manngo Sep 25 '18 at 20:12

1 Answers1

3

The rules of how Python parses expressions are defined in the Python grammar. Note that the types are irrelevant. In fact Python is dynamically types, so that means that at the time an expression is parsed and analyzed, the types of the variables is unknown. In fact a variable have different values (with different types) through the process, and a line can be evaluated multiple times, with the variables each time carrying a value of a different type.

If we take a look at the grammar, we see:

expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom_expr ['**' factor]
atom_expr: ['await'] atom trailer*
atom: ('(' [yield_expr|testlist_comp] ')' |
       '[' [testlist_comp] ']' |
       '{' [dictorsetmaker] '}' |
       NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME

The "subscription" (the [1] in b[1] is thus defined in a production rule of trailer, and if we look at the grammar, this can only be the product of a factor, so that means that the / operator takes precedence over the subscription.

So that means that:

a += b[1] / 2

is equivalent to:

a += ((b[1]) / 2)

Note that since Python is dynamically typed, the parsing (and analyzing) step will not give any guarantees that the expression is sensical. For example a tuple can not be devided by two. So this will result in a TypeError:

>>> (2.0, 4.0) / 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'tuple' and 'int'

For a numpy array however, this makes sense:

>>> from numpy import array
>>> array([2.0, 4.0])/2
array([1., 2.])
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555