[Python-Dev] Splitting something into two steps produces different behavior from doing it in one fell swoop in Python 2.6.2

Nick Coghlan ncoghlan at gmail.com
Sat Dec 12 01:59:49 CET 2009


Roy Hyunjin Han wrote:
> On Fri, Dec 11, 2009 at 2:43 PM, MRAB <python at mrabarnett.plus.com> wrote:
>> John Arbash Meinel wrote:
>>> y[1].update(y.pop(1))
>>>
>>> is going to be evaluating y[1] before it evaluates y.pop(1).
>>> Which means that it has the original set returned, which is then removed
>>> by y.pop, and updated.
>> To me the question was whether Python was behaving correctly; the
>> behaviour was more subtle than the legendary mutable default argument.
> 
> Thanks, John and MRAB.  I was pointing it out on this list because I
> felt like it was counterintuitive and that the result should be the
> same whether the developer decides to do it in two steps or in one
> step.

It follows the standard left-to-right evaluation order within an expression:

<subexpr1>(<subexpr2>)

(i.e. a function call always determines which function is going to be
called before determining any arguments to be passed)

Splitting it into two lines then clearly changes the evaluation order:

temp = <subexpr2>
<subexpr1>(temp)

I'm not sure what behaviour could be suggested as being more intuitive -
the problem in this case arose due to both referencing and mutating the
same object in a single statement, which is always going to be
problematic from an ordering point of view, since it depends on subtle
details of statement definitions that people often won't know. Better to
split the mutation and the reference into separate statements so the
intended order is clear regardless of how well the reader knows the
subtleties of Python's evaluation order.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------


More information about the Python-Dev mailing list