strange behavor....
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Sat Nov 13 17:32:39 EST 2010
On Sat, 13 Nov 2010 20:01:42 +0000, Mark Wooding wrote:
> Terry Reedy <tjreedy at udel.edu> writes:
>
>> On 11/13/2010 11:29 AM, Mark Wooding wrote:
>>
>> > Alas, Python is actually slightly confusing here, since the same
>> > notation `=' sometimes means assignment and sometimes means mutation.
>>
>> I disagree somewhat. An object is mutated by an internal assignment.
>
> Some object types are primitive, provided by the runtime system; there
> are no `internal' variables to be assigned in these cases.
You seem to be making up your own terminology here, or at least using
terminology that isn't normally used in the languages I'm used to. In OO
languages such as Python, Java, and (I believe) Ruby, it is conventional
to distinguish between objects and "primitives". Primitives are
implemented as low-level C-like variables.
In that sense, Python has no primitives. Ints, floats, etc. (which are
primitives in Java) are full-blown objects in Python.
If you mean "primitive" in the sense of "built-in", your conclusion is
wrong. Such "primitive" types include dicts and lists, which are
sophisticated container objects with internal "variables" to be assigned
to: alist[0] = None assigns the value None to the first position in the
list. Just because these "variables" aren't *named* variables doesn't
mean they don't vary, or that they can't be assigned to.
If you mean "primitive" in some other sense, I'm afraid I can't guess
what you mean.
>> "ll[0] = 1" assigns 1 to the 0 slot of ll. "o.a = 1" assigns 1 to the
>> 'a' attribute of o. This which might be implemented by assigning 1 to
>> the 'a' slot of o.__dict__, just as "a=1" might be implemented by
>> assigning 1 to the 'a' slot of a namespace dict.
>
> There's a qualitative difference here: simple assignment has semantics
> defined by the language and provided by the implementation, and can
> therefore be understood in isolation, using only lexically apparent
> information; whereas the complex assignments are implemented by invoking
> methods on the objects mentioned on the left hand side.
Again, you're using unfamiliar terminology. "Simple" and "complex"
assignment -- I can guess you mean "name binding" = simple and "any other
reference binding" = complex in Python terminology:
name = value # simple assignment
dict[key] = value # complex assignment
obj.attr = value # complex
The thing is, the semantics of assigning to built-in components are
equally defined by the language as the semantics of name binding. If x is
a built-in list or dict, the semantics of:
x[something] = value
is written in stone and part of the language semantics. Whether that is
implemented by a method or not is an irrelevant implementation detail,
but for what little it's worth, I believe the CPython implementation is
written in a fully procedural fashion, with no methods in sight.
One can write:
[0, 1, 2, 3, 4][2] = 5
and the language semantics tell you *exactly* what will happen: the value
of the list in position 2 will be unbound and the object 5 will be bound
in its place. Who cares whether the *implementation* happens to be
written as a C function or a Java method, or something else?
You are right to point out that for arbitrary types:
x[2] = 5
need not be an assignment, since if x is not a built-in x.__setitem__
will be called, and it may do anything it likes. By contrast, name
binding:
x = 5
*never* calls a method on any object, and therefore is fully defined by
the language semantics. The distinction between the two count as an
important proviso to the discussion: x[2] = 5 is only an assignment if
the type of x is non-pathological and not broken.
But putting aside such pathological cases, I don't think you can justify
distinguishing between "simple" and "complex" assignment. Names are only
one type of reference in Python, not the only one, and assignments other
than name-binding can be fully understood from the language semantics
*provided you know the type is a built-in*.
>> Assignment *always* binds an object to a target.
>
> No! Assignment /never/ binds.
A shocking claim that requires more explanation. If it doesn't bind, what
does it do?
> There is syntactic confusion here too,
> since Python interprets a simple assignment in a function body -- in the
> absence of a declaration such as `global' to the contrary -- as
> indicating that the variable in question should be bound to a fresh
> variable on entry to the function.
Well, there seems to be some confusion here, but I don't think it's
ours... local variables in functions aren't bound on *entry* to the
function. How could they be? They are bound *when the assignment is
executed*, which may be never -- hence it is possible to get an
UnboundLocalError exception, if you try to retrieve the value of a local
which hasn't yet had a value bound to it.
> But assignment itself doesn't
> perform binding. (This is a persistent error in the Python community;
> or, less charitably, the Python community gratuitously uses the word in
> a different sense from the wider programming-language-theory community.
> See Lisp literature passim, for example.)
*Less* charitably? I'm sorry, you think that being *wrong* is better than
being *different*? That's not a moral judgment I can agree with.
> There's a two step mapping: names -> storage locations -> values.
> Binding affects the left hand part of the mapping; assignment affects
> the right hand part.
That gratuitously conflates the implementation ("storage locations") with
the interface. Objects in Python have no storage location, they float in
the aether, free to move if the implementation chooses to move them...
the CPython implementation doesn't, but some other implementation might
do so, but either way *you can't tell the difference* from Python.
There is nothing you can write in pure Python that can tell whether the
storage location of an object has changed, or even whether "storage
location" is a well-defined concept. Nothing in the semantics of Python
demand that objects must be implemented as single contiguous blocks of
data with a well-defined location. If you can think of an alternative,
you're free to write an implementation that uses it.
The closest you can come is that CPython exposes the memory location as
the id(), but that's not a language promise: Jython and IronPython do not.
--
Steven
More information about the Python-list
mailing list