[Tutor] lists, name semantics

Dave Angel davea at davea.name
Sun Apr 19 13:47:59 CEST 2015


On 04/19/2015 12:07 AM, boB Stepp wrote:

.....
Before Peter changed one of these
> changeable objects, he had:
>
> a = [1, ["x", "y"], 3]
> b = a[:]
>
> Now BOTH a[1] and b[1] now identify the location of the inner list
> object, ["x", "y"] . Apparently, Python, in its ever efficient memory
> management  fashion, when it creates the new object/piece of data
> a[:], it sees no need to duplicate the inner list object, ["x", "y"],
> but instead creates another identifier/pointer/reference to this
> object's location. But since this inner list object is mutable, when
> you change "y" to "hello!" in b, you also change it in a because both
> a[1][1] and b[1][1] reference/point to the exact same storage location
> where this element of the inner list is actually stored.
>
> I hope this is helpful, and, if there are any misstepps, that when
> they are revealed both of our understandings will be enhanced!
>

Some of your knowledge of other languages is leaking into your 
explanation.  When we talk of the language Python, we need to 
distinguish between how CPython happens to be implemented, how other 
Python implementations happen to be created, and how C++ (for example) 
implements similar things.  Some of the above use pointers, some do not. 
  The language Python does not.

So especially when talking of inner lists, we need to clarify a few things.

An object has an identity, not a location.  That identity can be checked 
with the 'is' operator, or the id() function.  But it exists all the time.

Variables, as you say, do not contain an object, they reference it.  And 
the formal term for that is binding.  A name is bound to an object, to 
one object, at a time.

Now some objects have attributes, which is to say names, and those 
attributes are bound to other objects.  So if we define a class, and 
have an instance of that class, and the instance has attributes, we can 
do something like:
     obj.inst_name

and get the particular attribute.

Still other objects have unnamed bindings.  The canonical example is a 
list.  A list object has a bunch of bindings to other objects.  Even 
though each  binding doesn't have a specific name, it nevertheless 
exists.  And in this case we use integers to specify which of those 
bindings we want to follow.  And we use a special syntax (the square 
bracket) to indicate which of these we want.

So let's take the simplest example:

mylist = ["a", "b"]

the name mylist is bound to a list object, which has two numbered 
bindings, called 0 and 1.  That object's  [0] binding is to a separate 
object "a".  And the [1] binding is to a separate object "b"

There is another shorthand called a slice, which looks like [start, len, 
step] which lets us construct a *new* list from our list.  And using 
defaults for all three terms lets us copy the list.  But let's look at 
what the copy means:

newlist = mylist[:]
         -->  mylist[0, 2, 1]

This constructs a new list from the present one, where the zeroth 
location is bound to whatever object the first list's zeroth location 
was bound to.  And so on for the oneth location, the twoth, etc.

Only one new list object is built, and no new objects are made for it. 
It just gets new bindings to the same things the first list had.

At this point, it should be clear what a shallow copy means.  If the 
original list's oneth item was a binding to another list object, *that* 
list object does NOT get copied.

I don't like the term "inner list",  but I don't know if it's incorrect. 
  It's just misleading, since to the slice operation, the fact that it's 
a list is irrelevant.  It's just an object whose binding is to be copied.

-- 
DaveA


More information about the Tutor mailing list