Addressing the last element of a list

B

Bengt Richter

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
If you are willing to keep your "variables" in a namespace, it's easy
to make "pointers" that you can dereference like @b, except
-- given a preliminary:
(see PNS from http://groups.google.com/group/comp.lang.python/msg/54a7ab01906683ca )
... def __call__(self, name): return PNS(self, name)
...
-- and then you have to spell it a little differently:
7


For another slant, laundering all ns assignments through pickle dumps/loads
to get a fresh value so as not to share references, and simulate value
assignment sematics (I think? ;-/)

http://groups.google.com/group/comp.lang.python/msg/f29cf8e25b42d5cb

with a different spelling for pointer use (ns['a'] instead of ns('a') above,
and b[:] instead of b.v)

The class also gave you a way to spell the typical C address/pointer/deref ops.
It's based on a name space class that gives you a name space object (say ns)
for the "variables" that you can get "pointers" to like ptr = &var and which
you can pass around and use with dereferencing like *ptr for either assignment
or on the right hand side. Once you have a names space ns = NSHorne() you can
set variables in the ordinary way as attributes, or get "pointers" and use
them via *p semantics
>>> from ns_horne import NSHorne
>>> ns = NSHorne()
>>> ns.x = 'x value'
>>> ptr = ns['x']
>>> ptr[:]
'x value'

Now make a function that will use the pointer ... p[:] = v
...
Check indirect result
>>> ns.x 123
>>> ptr[:] 123
>>> ptr[:] += 321
>>> ns.x
444

Pseudo-value-semantics:
>>> from ns_horne import NSHorne
>>> ns = NSHorne()
>>> ns.z = [1,2]
>>> pz = ns['z']
>>> pz[:] [1, 2]
>>> ns.z2 = pz[:]
>>> ns.z [1, 2]
>>> ns.z2 [1, 2]
>>> pz[:][0]='z via pz'
>>> ns.z ['z via pz', 2]
>>> ns.z2 [1, 2]
>>> pz[:]
['z via pz', 2]

Or value semantics without confusing with pointer stuff:
now equal values, but not the same objects:
>>> ns.z3, ns.z2 ([1, 2], [1, 2])
>>> ns.z2[1]='z2 via ns.z2'
>>> ns.z3 [1, 2]
>>> ns.z2
[1, 'z2 via ns.z2']

Regards,
Bengt Richter
 
A

Antoon Pardon

Op 2005-11-14 said:
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.

That is possible.
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.

You have a valid point, but I think a different approach is possible.
Don't make the distinction between mutable and immutable types but
between mutable and immutable objects. So an int wouldn't be an
immutable type, but 5 would be an immutable object.

So the code above I gave would throw an exception, but the following
might work.

a @= 5
b = a
b @= 7
a ==> would result in 7.
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

Could it also work the other way? By somehow manipulating b change a?
 
A

Antoon Pardon

Op 2005-11-14 said:
Op 2005-11-14 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 ;-)

It is a rather long thread. You want to avoid this one becomes of
comparable length?
 
C

Chris Mellon

Op 2005-11-14 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 ;-)

I read that thread, and am glad I did before asking like I was going
to, if only to avoid being yelled at for not understanding Python
variables ;)

I understand them, I really do, and I know why they act the way they
do, but I still wanted a reference type for binding GUI objects to
data values - for example, a spinner control to an int value. In C++,
I do it by passing a pointer to an int to the control and letting it
do its thing. Theres no simple obvious way to do the same thing in
Python - I ended up by passing a namespace(any object) and an
attribute (by name) and then setting it via setattr in in the control,
which is non-obvious and icky.
 
B

Bengt Richter

Op 2005-11-14, Bengt Richter schreef <[email protected]>: [...]
You may be interested in reviewing

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

before continuing this topic ;-)

It is a rather long thread. You want to avoid this one becomes of
comparable length?
Not necessarily. I just thought it might help in making new discussions
of previously discussed ideas clearer faster ;-)

Sometimes rehashing old topics seems just tedious, but sometimes someone
has a hunch that they are struggling to express, and they haven't yet succeeded
in communicating its essence. Then old discussions can sometimes serve
like first drafts of a speech, and revisions can prevent repeat of misunderstandings
that in the archived discussions one can see were inevitable.

If the nascent idea comes out viable, we all benefit, and the tedium will have been
worth it. If not, well, then at least some ideas will have clearer form, and
the whole process will have been a social event that some will have enjoyed anyway ;-)

Regards,
Bengt Richter
 
M

Mike Meyer

Antoon Pardon said:
You have a valid point, but I think a different approach is possible.
Don't make the distinction between mutable and immutable types but
between mutable and immutable objects. So an int wouldn't be an
immutable type, but 5 would be an immutable object.
So the code above I gave would throw an exception, but the following
might work.
a @= 5
b = a
b @= 7
a ==> would result in 7.

Which solves that issue but brings up - well, more issues. What
happens if the third line is 'b @= "seven"'? Does this require an
extra level of indirection in the implementation? Avoiding that kind
of thing was the reason for suggesting this:
Could it also work the other way? By somehow manipulating b change a?

It's intended to work the other way as well. But the second line is
wrong: it should have been "b = a". "b = @a" assigns b the value
referenced by a, and @b would then be an error. "b = a" assigns b to
the reference that is a, so that @b returns it's value.

The critical thing is that this doesn't introduce any new facilities
into the language, just some new syntax, so there's no implementation
impact. In fact, if you're willing to put up with some notational
abuse, we can do this now:

class Ref(object):
_unbound = object()
def __new__(cls, value = _unbound):
"""We're an object, but need to ignore the optional argument."""

return object.__new__(cls)

def __init__(self, value = _unbound):
"""Bind the optional value, if provided."""
if value is not self._unbound:
self._value = value

def __pos__(self):
"""Return value, if bound."""
try:
return self._value
except AttributeError:
raise ValueError, "%s object does not have a value stored." % \
self.__class__.__name__

def __iadd__(self, value):
self._value = value
return self

Usage:
'this is a test'

Since it doesn't have real lannguage support, things like +x += 25
don't work. That's the only obvious gotcha. Maybe ~ would be a better
prefix.

<mike
 
B

Bengt Richter

I read that thread, and am glad I did before asking like I was going
to, if only to avoid being yelled at for not understanding Python
variables ;)

I understand them, I really do, and I know why they act the way they
do, but I still wanted a reference type for binding GUI objects to
data values - for example, a spinner control to an int value. In C++,
I do it by passing a pointer to an int to the control and letting it
do its thing. Theres no simple obvious way to do the same thing in
Python - I ended up by passing a namespace(any object) and an
attribute (by name) and then setting it via setattr in in the control,
which is non-obvious and icky.
ISTM that there are two main issues involved.
1) Can you code your intentions with some clean syntax?
1a) Is there clean syntax to help you think more fluently?
2) Is the implementation efficient enough for your use case?

(Ok, I couldn't help introducing 1a, because I think that is
where Python shines).

I think 2) can be ignored until we have a good proposal for 1) ;-)

IIUC what you want boils down to wanting to replace a value
via a reference object that you pass to a function, where the value
is known by name in some namespace, and you don't want
to use a (namespace, namestring) tuple as a reference object.

Actually, in general the value could be known by some access expression
that doesn't necessarily end in a name, like a[42]. Our magic ref expression
(let's use function syntax for now for creating the reference) becomes
more tricky. We now also have ref(a[42]) as well as ref(name).

For easy discussion of the concept, let's assume that refobj = ref(expr) works
for some subset of exprs and refobj lets us access the expr target via
refobj.value for assignment or in an expression. Note that the name 'value'
is not the name in ref(name), but is always 'value'. We can discuss later
whether we want syntactic sugar to spell refobj.value as @refobj or maybe
even plain refobj, but let's postpone that until we have agreed functionality
and some ideas on implementation.

So, to summarize, we have

refobj = ref(expr)

and we can assign to the indirect target and and use it in an expression

refobj.value = refobj.value + 1

with a fixed syntax that does not identify any of the original expression
(since "value" is an arbitrarily chosen mnemonic and attribute access is
an arbitrarily chosen accessor mechanism for discussion expediency).

For your example of ref(namespace.name) we could use my PNS class [1]
to get a refobj that acts as above, e.g.,

[1] http://groups.google.com/group/comp.lang.python/msg/54a7ab01906683ca

But how would ref(a[42]) work? By analogy we might have a class PLE (pointer
to list element) and instantiate it by refobj = PLE(a, 42). That would be easy.
One could even imagine a multimethod __init__ for a PXX class that would handle
a number of common ref(expr) situations, if you pass the appropriate pieces of expr
as args. One could imagine automating this in various ways. E.g., treating ref
as a keyword and automatically detecting it in a module's AST and translating
the call to pick apart the passed expression suitably to make a call that
generates the refobj. But this is premature implementation ;-)

Let's get the requirements down first. One thing that pops up immediately
if you think about ref(namespace.name) vs ref(a[42]) is preservation of
the dynamic access semantics whose execution is deferred in time. I.e.,
a dangling pointer problem with additional twists.

If the essential ref infos are (namespace, name) and (a, 42), we must note that
we really implicitly made it
(namespace, ('__getattr__', '__setattr__', '__delattr__'), name)
and
(a, ('__getitem__', '__setitem__', '__delitem__'), 42)

(at least I did in the way I defined PNS, because deferred use of the refobj
to set a value implied doing getattr(namespace, name) at that time.
Accessor methods could also be bound at ref call time, with different semantics
resulting. I.e., we could have ref info stored as

((namespace.__getattr__, namespace.__setattr__, namespace.__delattr__), name)
and
((a.__getitem__, a.__setitem__, a.__delitem__), 42)

What is the real meaning desired for a refobj ? How should ref(42) be handled?
Should it become a legal refobj that only fails if used as assignment target,
so that it can be passed transparently to read-only users as well as references
to mutables that wind up only being used read-only?

Regards,
Bengt Richter
 
A

Antoon Pardon

Op 2005-11-15 said:
Op 2005-11-14, Bengt Richter schreef <[email protected]>: [...]
You may be interested in reviewing

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

before continuing this topic ;-)

It is a rather long thread. You want to avoid this one becomes of
comparable length?
Not necessarily. I just thought it might help in making new discussions
of previously discussed ideas clearer faster ;-)

Well I hope you are right. But I must confess I skimmed a number of
articles, there were just too many, to read them all in such a short
times.
Sometimes rehashing old topics seems just tedious, but sometimes someone
has a hunch that they are struggling to express, and they haven't yet succeeded
in communicating its essence. Then old discussions can sometimes serve
like first drafts of a speech, and revisions can prevent repeat of misunderstandings
that in the archived discussions one can see were inevitable.
If the nascent idea comes out viable, we all benefit, and the tedium will have been
worth it. If not, well, then at least some ideas will have clearer form, and
the whole process will have been a social event that some will have enjoyed anyway ;-)

Well I certainly support that idea.
 
A

Antoon Pardon

Op 2005-11-15 said:
Which solves that issue but brings up - well, more issues. What
happens if the third line is 'b @= "seven"'? Does this require an
extra level of indirection in the implementation? Avoiding that kind
of thing was the reason for suggesting this:

It depends on how far you want to go.

I think one can argue that in case of an inplace replacement, this
only makes sense if the two objects belong to the same class. So
no extra level of indirection is then required.

Otherwise it really depends on how the builtin objects are implemented.
For user classes, a somewhat simplistic implementation could be
something like:

def '@=' (one, two):
one.__dict__.clear()
one.__dict__.update(two.__dict__)

But if such an approach is usable with the builtin classes, I don't
know. I'm sure this will have it's own issues though.
It's intended to work the other way as well. But the second line is
wrong: it should have been "b = a". "b = @a" assigns b the value
referenced by a, and @b would then be an error. "b = a" assigns b to
the reference that is a, so that @b returns it's value.

The critical thing is that this doesn't introduce any new facilities
into the language, just some new syntax, so there's no implementation
impact. In fact, if you're willing to put up with some notational
abuse, we can do this now:

class Ref(object):
_unbound = object()
def __new__(cls, value = _unbound):
"""We're an object, but need to ignore the optional argument."""

return object.__new__(cls)

def __init__(self, value = _unbound):
"""Bind the optional value, if provided."""
if value is not self._unbound:
self._value = value

def __pos__(self):
"""Return value, if bound."""
try:
return self._value
except AttributeError:
raise ValueError, "%s object does not have a value stored." % \
self.__class__.__name__

def __iadd__(self, value):
self._value = value
return self

Usage:

'this is a test'

Since it doesn't have real lannguage support, things like +x += 25
don't work. That's the only obvious gotcha. Maybe ~ would be a better
prefix.

I'm a bit puzzled on how you would implement this as real language
support. As far as I understand this only works with Ref objects.

You can't do something like the following.

l = [3, 7]
a = Ref(l[0])

Instead you would have to do something like
l = [Ref(3), Ref(7)]
a = l[0]

So it seems that if you want to use this idea for implementing langauge
support you will have to wrap all objects in a Ref internally and this
seems some kind of extra indirection too.
 
M

Mike Meyer

Antoon Pardon said:
It depends on how far you want to go.

I think one can argue that in case of an inplace replacement, this
only makes sense if the two objects belong to the same class. So
no extra level of indirection is then required.

Now factor in inheritance. Do users of this facility have to worry
about static OO typing? That is, two objects that are otherwise
completely interchangeable will raise an exception if you try to
assign one to a variable holding the other, because they have the
wrong type. That seems unpythonic.
Otherwise it really depends on how the builtin objects are implemented.
For user classes, a somewhat simplistic implementation could be
something like:

def '@=' (one, two):
one.__dict__.clear()
one.__dict__.update(two.__dict__)

Wouldn't one.__dict__ = dict(**two.__dict__) be a bit better?

Anyway, this won't' work if the class of one of the objects has
slots.
I'm a bit puzzled on how you would implement this as real language
support. As far as I understand this only works with Ref objects.

Correct. That's the point.
You can't do something like the following.

l = [3, 7]
a = Ref(l[0])

Instead you would have to do something like
l = [Ref(3), Ref(7)]
a = l[0]

So it seems that if you want to use this idea for implementing langauge
support you will have to wrap all objects in a Ref internally and this
seems some kind of extra indirection too.

No, the idea is to expose this class to the user. They would have to
explicitly wrap objects that they want to be able to be get a
reference to. Other object types can be implemented without having to
worry about assigning to them. If you want to assign to an object, you
cdreate it as a Ref. You still have to have a special syntax so you
can distinguish assigning to a Ref from binding a name, and getting
the value of the Ref vs. getting the Ref.

<mike
 
D

Duncan Booth

Mike said:
Wouldn't one.__dict__ = dict(**two.__dict__) be a bit better?

Those stars are redundant here. It would be cleaner to write:

one.__dict__ = dict(two.__dict__)
 
A

Antoon Pardon

Op 2005-11-16 said:
Now factor in inheritance. Do users of this facility have to worry
about static OO typing? That is, two objects that are otherwise
completely interchangeable will raise an exception if you try to
assign one to a variable holding the other, because they have the
wrong type. That seems unpythonic.

I don't care much about pythonic or not. It seems a very volatile
concept, that adapts as the language evolves. I wouldn't be
surprised if augmented arithmetic operations would have been
considered unpythonic at some point. You could see this
limitation as a wart, but IMO the idea would still be usefull
with that limitation.

The limitation would ensure for example that although the value
of an argument could change, it's class couldn't. I think that
is worthwhile.
Wouldn't one.__dict__ = dict(**two.__dict__) be a bit better?

Anyway, this won't' work if the class of one of the objects has
slots.

So? I wrote it was a simplistic idea. but things like setattr and
getattr, still work with objects that have slots. So which attributes
are present for a particulare object has to be available in the
python interpreter. So my guess is that slots wont be the big stumbling
block, should one consider implementing something like this.
I'm a bit puzzled on how you would implement this as real language
support. As far as I understand this only works with Ref objects.

Correct. That's the point.
You can't do something like the following.

l = [3, 7]
a = Ref(l[0])

Instead you would have to do something like
l = [Ref(3), Ref(7)]
a = l[0]

So it seems that if you want to use this idea for implementing langauge
support you will have to wrap all objects in a Ref internally and this
seems some kind of extra indirection too.

No, the idea is to expose this class to the user. They would have to
explicitly wrap objects that they want to be able to be get a
reference to.

Well that seems less usefull to me.

Let me explain, what I would like it for.

Sometime I'm programming with tree like structures. Occasionaly such
a tree has to be restructured. Now of course I could write it like
this:

def restruct(tr):

if ...:
tr.left = restruct(tr.left)
tr.right = restruct(tr.right)
tmp = tr.left
tr.left = tmp.right
tmp.right = tr
return tmp.

...
tree = restruct(tree)

But this feels wrong to me. It gives the impression that the following
would give you a new restructured tree while the old one is still
available, which isn't so:

newtree = restruct(tree)

IMO being able to write:

restruct(tree)

With as a result that tree would now be the new root of the restructered
tree, would better convey what is going on. Now having to make my tree
object into a ref, which would mean all nodes in the tree, seems
overkill. Maybe we can consider in-out parameters in python? It is
not as powerfull as reference variables or in place replacements
but from what I have seen here in the newsgroup would be sufficient
for most users that ask for something like this.
 

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,279
Latest member
LaRoseDermaBottle

Latest Threads

Top