The meaning of "=" (Was: tough-to-explain Python)

Aahz aahz at
Fri Jul 10 15:50:29 CEST 2009

[excessive quoting ahead, I'm too tired to trim]

In article <h32i2o$eop$1 at>, kj  < at> wrote:
>In <h32fon$264$1 at> aahz at (Aahz) writes:
>>In article <h32eoh$ql8$1 at>, kj  < at> wrote:
>>>OK, so, scratching from my original post the case
>>><identifier>.<identifier> = <expression>
>>>(as being a special case of <identifier> = <expression>), still,
>>>to the extent that I understand your post, the "=" in
>>>  x = 1
>>>means something fundamentally different (in terms of Python's
>>>underlying implementation) from the "=" in
>>>  y[0] = 1
>>No.  ;-)
>No???  Just when I thought I finally understood all this!
>>What's different is not the ``=`` but the construction of the
>>assignment target before ``=`` gets executed.
>Hmm.  OK, I went to the link you posted in your other message
>and I find this (my emphasis):
>    Assignment of an object to a single target is recursively defined as follows.
>    * If the target is an identifier (name):
>	  o If the name does not occur in a global statement in
>	    the current code block: the name is bound to the object
>                                    ^^^^^^^^^^^^^^^^^
>	    in the current local namespace.
>	  o Otherwise: the name is bound to the object in the
>                       ^^^^^^^^^^^^^^^^^
>	    current global namespace.
>      The name is rebound if it was already bound. This may cause
>      the reference count for the object previously bound to the
>      name to reach zero, causing the object to be deallocated and
>      its destructor (if it has one) to be called.
>    * If the target is a target list enclosed in parentheses or in
>      square brackets... (I'LL IGNORE THIS FOR NOW)
>    * If the target is an attribute reference: The primary expression
>      in the reference is evaluated. It should yield an object with
>      assignable attributes; if this is not the case, TypeError is
>      raised. That object is then asked to assign the assigned
>              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>      object to the given attribute; if it cannot perform the
>      ^^^^^^
>      assignment, it raises an exception (usually but not necessarily
>      AttributeError).
>    * If the target is a subscription: The primary expression in
>      the reference is evaluated. It should yield either a mutable
>      sequence object (such as a list) or a mapping object (such
>      as a dictionary). Next, the subscript expression is evaluated.
>      If the primary is a mutable sequence object (such as a
>      the sequence is asked to assign the assigned object to its
>      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>      item with that index....
>      If the primary is a mapping object (such as a dictionary),...
>                                                          ^^^
>      mapping is then asked to create a key/datum pair which maps
>      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>      the subscript to the assigned object.
>      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>    * If the target is a slicing: [INDEX STUFF OMITTED]... the
>                                                           ^^^
>      sequence object is asked to replace the slice with the items
>      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>      of the assigned sequence...
>      ^^^^^^^^^^^^^^^^^^^^^^^^
>OK, I originally interpreted what Lundh wrote in his two articles
>that the "binding" described at the very beginning (i.e. when the
>target is an identifier), which I take to make an entry or modify
>such an entry in a namespace, is a *categorically different*
>operation from the remaining operations underlined above.
>I interpreted Paul Boddie's correction in his response to me as
>saying that the "assignment" mentioned for the case when the target
>is an attribute reference is actually a special case of the
>"assignment" to simple identifiers (i.e. it also means "binding").
>But that still leaves all the other "assignments" (or the like)
>underlined above.  I don't think that the full definitions of these
>remaining cases are covered by the same rule, even though the rule
>is described as "recursive."  I think that the writer has something
>else in mind, and in particular, something *other* than binding,
>but the author remains vague on exactly what this means.
>Clearly, both Lundh and the documentation draw some distinction
>between "binding" and some other forms of "assignment" (which remain
>ill-defined throughout).  This distinction is what I was referring
>to when I said that "=" means different things in different contexts.

Consider this:

x = 1
globals()['x'] = 1
locals()[1] = 1

What's the difference between the three?  Although there's a lot of
machinery amenable to manipulation, with the corresponding potential for
breaking the standard model, fundamentally it all comes down to the
intention that *some* piece of code establishes a target on the left-hand
side and binds it to the object returned by the right-hand side.  IME,
except when going down to the very lowest-level details, Python is
easiest to understand when you treat all assignment statements as working
the same.  It helps to remember that names and namespaces are in many
ways syntactic sugar for dicts or lists.

(Obviously, the example with locals() doesn't do what it purports to do,
but it shows how function/method namespaces work under the covers.)

Slicing is the one real exception, but again I think that it's easier to
understand by wedging your mental model into conformance with the simple
target/binding metaphor.
Aahz (aahz at           <*>

"as long as we like the same operating system, things are cool." --piranha

More information about the Python-list mailing list