Addressing the last element of a list

S

Steven D'Aprano

So there is no way in Python to make an alias for an object?

Yes, sort of. Bind two names to the same mutable object:

py> x = ["Something mutable"]
py> y = x
py> y.append("this way comes.")
py> print x
['Something mutable', 'this way comes.']

Note that this only works with mutable objects like lists and dicts, and
is a side effect of mutability, rather than a deliberate "alias".

In general, you can bind multiple names to the same object. The one liner

x = y = 1

is the same as the two liner

x = 1
y = x

Both create two names, x and y, and sets them both to the same int 1.

Because ints are immutable, if you rebind one name, the other one will
NOT change:

py> x += 1
py> print x, y
2, 1
 
S

Steven D'Aprano

Or alternatively:

Is there a way to make reference to the last element of a list, to use
as a shorthand:

ref := &lst[len(lst) - 1] # I know syntax is wrong, but you get the
idea

and then using the last element of the list by (assuming the element is
a dict):

ref["foo"] = 42
ref["bar"] = "Ni!"

py> lst = ["Now is the time", 4, "all good men", {"foo": "bar"}]
py> lst[-1] # refers to the last item of lst
{'foo': 'bar'}
py> obj = lst[-1] # the name obj is now bound to the same dict
py> obj["foo"] = 42
py> print lst
['Now is the time', 4, 'all good men', {'foo': 42}]

This only works with mutable objects. See:

py> obj = lst[-3]
py> obj
4
py> obj = obj + 1 # rebinds the name obj, doesn't change the underlying
object the name points to
py> print lst
['Now is the time', 4, 'all good men', {'foo': 42}]

You will quickly learn what are mutable and what are immutable. Ints,
floats, longs, strs and tuples are immutable. Lists and dicts are mutable.
Custom classes could be either, in principle, but are generally mutable.
 
S

Steven D'Aprano

But if lst[42]["pos"] happens to hold an integer value, then

a = lst[42]["pos"]

will _copy_ that integer value into 'a', right? Changing 'a' will not
change the value at lst[42]["pos"]

Not quite. Don't think of Python names as being like variables.

Think of it like this: in Python, everything is an object. You reference
objects by creating a name, and binding that name to an object:

py> parrot = {'feathers': 'beautiful plumage'}

This creates a name, parrot, and binds it to the dictionary given.

If the underlying object is mutable, like dicts and lists, you can modify
the object in place:

py> parrot['state'] = 'pining for the fjords'
py> parrot['breed'] = 'Norwegian Blue'
py> print parrot
{'feathers': 'beautiful plumage', 'state': 'pining for the fjords',
'breed': 'Norwegian Blue'}

But immutable objects can't be changed in place (that's what immutable
means). Ints are immutable objects, so you can't change the value of the
int object 1: 1 is always 1.

(Imagine the confusion if you changed the object 1 to have a value of 3;
you could say 1 + 2 and get 5. That would be a *really* bad idea.)

So when working with ints, strs or other immutable objects, you aren't
modifying the objects in place, you are rebinding the name to another
object:

py> spam = "a tasty meat-like food"
py> alias = spam # both names point to the same str object
py> spam = "spam spam spam spam" # rebinds name to new str object
py> print spam, alias
'spam spam spam spam' 'a tasty meat-like food'
 
D

Dennis Lee Bieber

But if lst[42]["pos"] happens to hold an integer value, then
lst[42]["pos"]

is ALREADY a reference to an integer value
a = lst[42]["pos"]
a

is NOW a reference to the same integer value
will _copy_ that integer value into 'a', right? Changing 'a' will not
change the value at lst[42]["pos"]

No... "a" now references the same integer... But in Python you can
NOT change an integer. So an subsequent assignment to "a"

a = xyz

doesn't change the integer, it changes "a" to NOW references whatever
object "xyz" references.


Time for the Post-It note simile.

Classical languages use the "post office sorting box" view. Each box
(variable name) has a fixed address, that can not be changed.

a = xyz

means "look in the box 'xyz', make a copy of what is in there, and put
that copy in the box 'a' (removing anything that had been in box 'a')"

In Python, the boxes (variable names) don't have fixed addresses.
Instead, the names are "on Post-It notes".

a = xyz

means "find the Post-It with 'a' written on it; if not found, create a
new Post-It. MOVE that Post-It from where ever you found it, to the box
that has a Post-It with 'xyz' written on it. If the location you'd found
'a' has NO Post-It notes on it, garbage collect it"

Note that: the object (integer, whatever) is NOT attached to the
name, rather the name is attached to the object -- one object can have
many names (after the above example, the object has two names minimum
"a" and "xyz" are both stuck to the object). If you now do an assignment
to one of the names, /that/ name gets /moved/ to the new object.
--
 
M

Michael

Is there a way to make reference to the last element of a list, to use
as a shorthand:

Yes. It's not wise to use, since this is brittle and will fail hard for you,
but since you're interested, this is how you /could/ do it: (largely)

# First of all create a mechanism for creating and handling symbolic
# references
class ref(object):
def __init__(self, val):
self._val = val
def set_val(self, val):
exec("global %s\n%s = %s" % (self._val, self._val, repr(val)))
def get_val(self):
return eval(self._val)
val = property(get_val, set_val)

Now we can play with this.

Note the word *PLAY* .
lst = ["1","2","3","4"]
y = ref("lst[-1]")
y.val '4'
y.val = 10
y.val 10
lst ['1', '2', '3', 10]
i = 1
z = ref("i")
z.val = 10
i
10

Once again, note the word *play* - don't use this in __anything__ :)
Python binds values to names. As a result what you're after when asking
for

ref := &lst[len(lst) - 1]

And then for ref to actually refer to that last item, you have to realise
that you're asking for an indirection on the name "lst[-1]". Short of doing
silly things with eval and exec (which you really don't want to do), you
can't do this.

However, it's python, so of course you *can*, but just because you *can* do
something doesn't mean that you *should*.

You'll note that in order to make the reference to the plain mutable
value (i) work the set_val had to mark the value as global, which isn't
quite right. (You might be able to trick it into using the "right" scope
using lambda somehow, maybe)

Once again, don't use it! (hopefully of interest though)

Regards,


Michael.
 
D

Donn Cave

Quoth Steven D'Aprano <[email protected]>:
....
| So when working with ints, strs or other immutable objects, you aren't
| modifying the objects in place, you are rebinding the name to another
| object:
|
| py> spam = "a tasty meat-like food"
| py> alias = spam # both names point to the same str object
| py> spam = "spam spam spam spam" # rebinds name to new str object
| py> print spam, alias
| 'spam spam spam spam' 'a tasty meat-like food'

The semantics of assignment are like that, period. If the right hand
side is an int, a string, a class instance, a list, whatever, doesn't
matter at all. The question of mutability at this point can be a red
herring for someone who doesn't already understand these matters.

Mutability is nothing special, it's just a feature built into the
object type -- in general, the ability to store some state. This
is of course useful in situations where we want to propagate state
changes, so it naturally comes up in this context, but language per
se does not observe any distinction here so far as I know.

Donn Cave, donn@drizzle.
 
S

Steven D'Aprano

Quoth Steven D'Aprano <[email protected]>:
...
| So when working with ints, strs or other immutable objects, you aren't
| modifying the objects in place, you are rebinding the name to another
| object:
|
| py> spam = "a tasty meat-like food"
| py> alias = spam # both names point to the same str object
| py> spam = "spam spam spam spam" # rebinds name to new str object
| py> print spam, alias
| 'spam spam spam spam' 'a tasty meat-like food'

The semantics of assignment are like that, period. If the right hand
side is an int, a string, a class instance, a list, whatever, doesn't
matter at all. The question of mutability at this point can be a red
herring for someone who doesn't already understand these matters.

Yes, but in the context we were discussing, the original poster was
specifically asking to do something that is only possible with mutable
objects.

He wanted to do something like this:

data = [0, None, 2, ["hello"]]
ref = data[-1]
ref.append("world")

and end up with [0, None, 2, ["hello", "world"]]. That will work, because
the last item in data is mutable. But this will NOT work:

data = [0, None, 2, 0]
ref = data[-1]
ref = 1
assert data[-1] == 1

because ints are immutable. So the distinction between modifying a
mutable object in place and assigning is important.
 
A

Antoon Pardon

Op 2005-11-10 said:
Quoth Steven D'Aprano <[email protected]>:
...
| So when working with ints, strs or other immutable objects, you aren't
| modifying the objects in place, you are rebinding the name to another
| object:
|
| py> spam = "a tasty meat-like food"
| py> alias = spam # both names point to the same str object
| py> spam = "spam spam spam spam" # rebinds name to new str object
| py> print spam, alias
| 'spam spam spam spam' 'a tasty meat-like food'

The semantics of assignment are like that, period. If the right hand
side is an int, a string, a class instance, a list, whatever, doesn't
matter at all. The question of mutability at this point can be a red
herring for someone who doesn't already understand these matters.

Yes, but in the context we were discussing, the original poster was
specifically asking to do something that is only possible with mutable
objects.

He wanted to do something like this:

data = [0, None, 2, ["hello"]]
ref = data[-1]
ref.append("world")

and end up with [0, None, 2, ["hello", "world"]]. That will work, because
the last item in data is mutable. But this will NOT work:

data = [0, None, 2, 0]
ref = data[-1]
ref = 1
assert data[-1] == 1

because ints are immutable. So the distinction between modifying a
mutable object in place and assigning is important.

I wonder couldn't this be done with properties?

Write somekind of property so that if you manipulate a.x it would
manipulate data[-1]
 
P

Peter Otten

Antoon said:
Write somekind of property so that if you manipulate a.x it would
manipulate data[-1]

Like that?
.... def get(self): return self[index]
.... def set(self, value): self[index] = value
.... return property(get, set)
........ first = make_prp(0)
.... last = make_prp(-1)
....
items = List([1,2,3])
items.first 1
items.last = 42
items
[1, 2, 42]

However, that's customizing attribute access, not name binding.

Peter
 
D

Daniel Crespo

Hi

Proposition 1:
data = [0, None, 2, 0]
ref = data[-1]
ref = 1
assert data[-1] == 1

Logically, it doesn't work. Here you are assigning to ref a NEW value.
That won't replace the data[-1] value. It's out of logic this
proposition.

While this (proposition 2):
data = [0, None, 2, ["hello"]]
ref = data[-1]
ref.append("world")

does work, because you are assigning to ref THAT list ["hello"]. So, if
you have THAT list, and appends a value, THAT list will be modified.
ref is pointing to THAT object. So, I don't understand why you write
the first proposition, like if that can be a normal thinking of how to
do the second proposition :-S

Well, I hope that newcomers to Python don't confuse himselves :)

Daniel
 
M

Mike Meyer

[Context recovered from top posting.]

This mutable/immutable object and name/variable is confusing.

Only if you have to overcome a conviction that variables behave in a
different way. If you've never seen them behave another way, or have
already gotten used to this model from another language (it dates back
to the 60s, if not the late 50s), then it's no problem. I'm sure the
problem exists in the opposite direction, except that few people
travel that route.

Most OO languages do the name/variable thing, but some of the popular
ones aren't consistent about it, giving some types "special" status,
so that sometimes "a = b" causes b to be copied onto a, and sometimes
it causes a to become a pointer to b. I find a consistent approach is
preferable.

Most OO languages also have the mutable/immutable object thing. The
set of which objects are immutable changes from language to
language. It's really only relevant in this case because the solution
to "I want to change an alias" issue involves using a mutable object.

<mike
 
B

bonono

After one knows the characteristic, it is no problem. But judging from
the frequency of the same issue coming up from time to time, it does
confuse people coming from certain programming background.
 
D

Donn Cave

....
Most OO languages do the name/variable thing, but some of the popular
ones aren't consistent about it, giving some types "special" status,
so that sometimes "a = b" causes b to be copied onto a, and sometimes
it causes a to become a pointer to b. I find a consistent approach is
preferable.

Who wouldn't.
Most OO languages also have the mutable/immutable object thing. The
set of which objects are immutable changes from language to
language. It's really only relevant in this case because the solution
to "I want to change an alias" issue involves using a mutable object.

Yes, and furthermore it's only vaguely relevant. I mean,
it really requires a particular kind of mutability, where one
object can store a reference to another. That's easy to find
in core object types, and of course it is a kind of mutability,
but it isn't the definition of mutable.

So we drag out this terminology, that neither clearly nor
accurately describes the functionality we have in mind,
and then we make some vague or even wrong statement about
its relationship to the issue. It has been going on for
years, usually I believe from people who understand quite
well how it really works.

Donn Cave, (e-mail address removed)
 
S

Steven D'Aprano

This mutable/immutable object and name/variable is confusing.

It is a source of confusion to newbies, because it is a distinction that
may not be intuitively obvious, and it is a programming model that isn't
quite the same as many people are used to.

One way that may help thinking about it is to imagine that Python names
are "like" pointers. That may make people feel less threatened, since
pointers in languages that use them often confuse beginners too.

As for mutable/immutable, that's simple too. Some objects can change their
value, and some objects can't.

But the best way to get an intuitive feel for how Python operates is to
start up an interactive session and just experiment!
 
A

Antoon Pardon

Op 2005-11-10 said:
[Context recovered from top posting.]

This mutable/immutable object and name/variable is confusing.

Only if you have to overcome a conviction that variables behave in a
different way. If you've never seen them behave another way, or have
already gotten used to this model from another language (it dates back
to the 60s, if not the late 50s), then it's no problem. I'm sure the
problem exists in the opposite direction, except that few people
travel that route.

Most OO languages do the name/variable thing, but some of the popular
ones aren't consistent about it, giving some types "special" status,
so that sometimes "a = b" causes b to be copied onto a, and sometimes
it causes a to become a pointer to b. I find a consistent approach is
preferable.

But what about a consistent approach, that allows choice.

Like having an assignment operator (let use @= for it) next to a
(re)bind operator.

We could then have something like the following.

a = 5
b = a
a @= 7
b ==> would result in 7.

I think such option is usefull and I sometimes miss it in python.
 
P

Paul Rubin

Antoon Pardon said:
We could then have something like the following.

a = 5
b = a
a @= 7
b ==> would result in 7.

Ouch! :-(((

Can't you live with

a = [5]
b = a
a[0] = 7

so b[0] is now 7.
 
A

Antoon Pardon

Op 2005-11-14 said:
Antoon Pardon said:
We could then have something like the following.

a = 5
b = a
a @= 7
b ==> would result in 7.

Ouch! :-(((

Can't you live with

a = [5]
b = a
a[0] = 7

so b[0] is now 7.

And what do I have to do, in case of the following:

a = [3, 5]
b = a[0]
b @= 7
a ==> would result in [7, 5]

This may seem contrived, but in combination with
parameters it could be usefull.

Something like:

a = [3, 5]

def treat(b):
lots of code
b @= new_value

f(a[0])
a ==> would result in [7, 5]
 
M

Mike Meyer

Antoon Pardon said:
Op 2005-11-10 said:
[Context recovered from top posting.]
Daniel Crespo wrote:
Well, I hope that newcomers to Python don't confuse himselves :)
This mutable/immutable object and name/variable is confusing.
Only if you have to overcome a conviction that variables behave in a
different way. If you've never seen them behave another way, or have
already gotten used to this model from another language (it dates back
to the 60s, if not the late 50s), then it's no problem. I'm sure the
problem exists in the opposite direction, except that few people
travel that route.
Most OO languages do the name/variable thing, but some of the popular
ones aren't consistent about it, giving some types "special" status,
so that sometimes "a = b" causes b to be copied onto a, and sometimes
it causes a to become a pointer to b. I find a consistent approach is
preferable.
But what about a consistent approach, that allows choice.

It's been done in other languages. But it requires more care than one
would think.
Like having an assignment operator (let use @= for it) next to a
(re)bind operator.

We could then have something like the following.

a = 5
b = a
a @= 7
b ==> would result in 7.

You've just overwritten the object referred to by the token "5" in the
source code with the value 7, so you get:

print 5
7

Not exactly a desirable outcome, but some language implementations
have allowed this kind of thing in the past. You either have to make
all objects mutable, or disallow assignment to immutable objects,
which sort of defeats the purpose.

Another approach is to have a special object type if you want to allow
assignment to the object it refers to. That's what Python does now, it
just doesn't have a syntax just to support that. If it did, your
example might look like:

a := 5
b = @a
a := 7
@b ==> would result in 7

<mike
 
B

Bengt Richter

Op 2005-11-14 said:
Antoon Pardon said:
We could then have something like the following.

a = 5
b = a
a @= 7
b ==> would result in 7.

Ouch! :-(((

Can't you live with

a = [5]
b = a
a[0] = 7

so b[0] is now 7.

And what do I have to do, in case of the following:

a = [3, 5]
b = a[0]
b @= 7
a ==> would result in [7, 5]

This may seem contrived, but in combination with
parameters it could be usefull.

Something like:

a = [3, 5]

def treat(b):
lots of code
b @= new_value

f(a[0])
a ==> would result in [7, 5]

--
You may be interested in reviewing

http://groups.google.com/group/comp...hread/thread/f96b496b6ef14e2/32d3539e928986b3

before continuing this topic ;-)

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top