[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Removing objects

NIm

1/23/2008 6:59:00 AM

I am writing a game, and it must keep a list of objects. I've been
representing this as a list, but I need an object to be able to remove
itself. It doesn't know it's own index. If I tried to make each object
keep track of it's own index, it would be invalidated when any object
with a lower index was deleted. The error was that when I called
list.remove(self), it just removed the first thing in hte list with
the same type as what I wanted, rather than the object I wanted. The
objects have no identifying charachteristics, other than thier
location in memory

So my question: How do I look something up in a list by it's location
in memory? does python even support pointers?

Is there a better way?
12 Answers

Asun Friere

1/23/2008 7:16:00 AM

0

On Jan 23, 5:59 pm, bladedpeng...@gmail.com wrote:
> I am writing a game, and it must keep a list of objects. I've been
> representing this as a list, but I need an object to be able to remove
> itself. It doesn't know it's own index. If I tried to make each object
> keep track of it's own index, it would be invalidated when any object
> with a lower index was deleted. The error was that when I called
> list.remove(self), it just removed the first thing in hte list with
> the same type as what I wanted, rather than the object I wanted. The
> objects have no identifying charachteristics, other than thier
> location in memory
>
> So my question: How do I look something up in a list by it's location
> in memory? does python even support pointers?
>
> Is there a better way?


How about adding an id attribute to your objects, which will contain a
unique identifier, override __eq__ to use that id to compare itself to
others and then simply pop off the object using
object_list.pop(object_list.index(self)). Something like this:

>>> class Spam (object) :
def __init__ (self, id) :
self.id = id
def __eq__ (self, other) :
try :
return self.id == other.id
except AttributeError :
return False


>>>
>>> a,b,c = Spam(1), Spam(2), Spam(3)
>>> x = [a,b,c]
>>> x.pop(x.index(c))
<__main__.Spam object at 0x885e5ac>

Except your object would be telling the list to pop self of course,
and you'd need someway of insuring the uniqueness of your IDs.

Robert Kern

1/23/2008 7:24:00 AM

0

bladedpenguin@gmail.com wrote:
> I am writing a game, and it must keep a list of objects. I've been
> representing this as a list, but I need an object to be able to remove
> itself. It doesn't know it's own index. If I tried to make each object
> keep track of it's own index, it would be invalidated when any object
> with a lower index was deleted. The error was that when I called
> list.remove(self), it just removed the first thing in hte list with
> the same type as what I wanted, rather than the object I wanted. The
> objects have no identifying charachteristics, other than thier
> location in memory

By default, classes that do not implement the special methods __eq__ or __cmp__
get compared by identity; i.e. "(x == y) == (x is y)". Double-check your classes
and their super-classes for implementations of one of these methods.
mylist.remove(x) will check "x is mylist[i]" first and only check "x ==
mylist[i]" if that is False.


In [1]: class A(object):
...: def __eq__(self, other):
...: print '%r == %r' % (self, other)
...: return self is other
...: def __ne__(self, other):
...: print '%r != %r' % (self, other)
...: return self is not other
...:
...:

In [2]: As = [A() for i in range(10)]

In [3]: As
Out[3]:
[<__main__.A object at 0xf47f70>,
<__main__.A object at 0xf47d90>,
<__main__.A object at 0xf47db0>,
<__main__.A object at 0xf47cb0>,
<__main__.A object at 0xf47eb0>,
<__main__.A object at 0xf47e70>,
<__main__.A object at 0xf47cd0>,
<__main__.A object at 0xf47e10>,
<__main__.A object at 0xf47dd0>,
<__main__.A object at 0xf47e90>]

In [4]: A0 = As[0]

In [5]: A0
Out[5]: <__main__.A object at 0xf47f70>

In [6]: As.remove(A0)

In [7]: As
Out[7]:
[<__main__.A object at 0xf47d90>,
<__main__.A object at 0xf47db0>,
<__main__.A object at 0xf47cb0>,
<__main__.A object at 0xf47eb0>,
<__main__.A object at 0xf47e70>,
<__main__.A object at 0xf47cd0>,
<__main__.A object at 0xf47e10>,
<__main__.A object at 0xf47dd0>,
<__main__.A object at 0xf47e90>]

In [8]: A0
Out[8]: <__main__.A object at 0xf47f70>

In [9]: A9 = As[-1]

In [10]: As.remove(A9)
<__main__.A object at 0xf47d90> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47db0> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47cb0> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47eb0> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47e70> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47cd0> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47e10> == <__main__.A object at 0xf47e90>
<__main__.A object at 0xf47dd0> == <__main__.A object at 0xf47e90>

In [11]: As
Out[11]:
[<__main__.A object at 0xf47d90>,
<__main__.A object at 0xf47db0>,
<__main__.A object at 0xf47cb0>,
<__main__.A object at 0xf47eb0>,
<__main__.A object at 0xf47e70>,
<__main__.A object at 0xf47cd0>,
<__main__.A object at 0xf47e10>,
<__main__.A object at 0xf47dd0>]

In [12]: A9
Out[12]: <__main__.A object at 0xf47e90>


If you cannot find an implementation of __eq__ or __cmp__ anywhere in your code,
please try to make a small, self-contained example like the one above but which
demonstrates your problem.

> So my question: How do I look something up in a list by it's location
> in memory? does python even support pointers?

If you need to keep an __eq__ that works by equality of value instead of
identity, then you could keep a dictionary keyed by the id() of the object. That
will correspond to its C pointer value in memory.

In [13]: id(A9)
Out[13]: 16023184

In [14]: hex(_)
Out[14]: '0xf47e90'

> Is there a better way?

Possibly. It looks like you are implementing a cache of some kind. Depending on
exactly how you are using it, you might want to consider a "weak" dictionary
instead. A weak dictionary, specifically a WeakValueDictionary, acts like a
normal dictionary, but only holds a weak reference to the object. A weak
reference does not increment the object's reference count like a normal
("strong") reference would. Consequently, once all of the "strong" references
disappear, the object will be removed from the WeakValueDictionary without your
having to do anything explicit. If this corresponds with when you want the
object to be removed from the cache, then you might want to try this approach.
Use "id(x)" as the key if there is no more meaningful key that fits your
application.

http://docs.python.org/lib/module-we...

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

Asun Friere

1/23/2008 7:54:00 AM

0

On Jan 23, 6:16 pm, Asun Friere <afri...@yahoo.co.uk> wrote:

> >>> x.pop(x.index(c))

Umm, of course you would simply use x.remove(c) ... force of (bad)
habit. %/



Patrick Mullen

1/23/2008 9:09:00 AM

0

On Jan 22, 2008 10:59 PM, <bladedpenguin@gmail.com> wrote:
> I am writing a game, and it must keep a list of objects. I've been
> representing this as a list, but I need an object to be able to remove
> itself. It doesn't know it's own index. If I tried to make each object
> keep track of it's own index, it would be invalidated when any object
> with a lower index was deleted. The error was that when I called
> list.remove(self), it just removed the first thing in hte list with
> the same type as what I wanted, rather than the object I wanted. The
> objects have no identifying charachteristics, other than thier
> location in memory
>
> So my question: How do I look something up in a list by it's location
> in memory? does python even support pointers?
>
> Is there a better way?

To put it simply, list.remove(self) ought to work. Are you sure it's
not working? list.remove self doesn't delete the first matching type,
it deletes the first object that IS what you passed, or failing that,
it deletes the first object that == what you pass.

I use an idiom often in my games, where rather than deleting objects
on the fly, which is not very thread-safe in case you have something
scanning through the objects, I set a property .kill on objects that
are about to die, and then rebuild the list in the main loop with only
objects that haven't been killed. I don't know if it's better, but it
would probably work in your situation if list.remove(self) isn't
working (which is strange).

The other benefit of my idiom, is that objects don't need to have a
reference to the object list, and any other function that wants to
delete that object needs no other references besides the object
itself. I have trouble with references a lot in complex games, and
where I can I prefer not to have them.

Helmut Jarausch

1/23/2008 11:21:00 AM

0

bladedpenguin@gmail.com wrote:
> I am writing a game, and it must keep a list of objects. I've been
> representing this as a list, but I need an object to be able to remove
> itself. It doesn't know it's own index. If I tried to make each object
> keep track of it's own index, it would be invalidated when any object
> with a lower index was deleted. The error was that when I called
> list.remove(self), it just removed the first thing in hte list with
> the same type as what I wanted, rather than the object I wanted. The
> objects have no identifying charachteristics, other than thier
> location in memory
>
> So my question: How do I look something up in a list by it's location
> in memory? does python even support pointers?
>
> Is there a better way?

You could use a doubly linked list.
The class 'deque' from collections is such a list
but I don't known how to delete a specific element without
this ugly (expensive?) rotate method.

Here is my solution in pure Python

#!/usr/bin/python

import re

class Queue(object):
__slots__= 'Head', 'Tail', '__iter__','NNd'

def __init__(self):
self.Head= None
self.Tail= None
self.NNd= None

def append(self,N):
if self.Head == None:
self.Head= N
self.Tail= self
N._enqueue_after(self.Tail)
self.Tail= N

def dequeue(self,N):
Father, Son= N._dequeue()
if self.Tail == N:
self.Tail= Father
if self.Head == N:
self.Head= Son
if self.Head == None: self.Tail= None

def enqueue_after(self,N,Father):
N._enqueue_after(Father)
if self.Tail == Father:
self.Tail= N

def enqueue_before(self,N,Son):
N._enqueue_before(Son)
if self.Head == Son:
self.Head= N

def __iter__(self):
next= self.Head # allows to dequeue the current element in a loop
while True:
current= next
if current == None: raise StopIteration
next= current._next()
yield current



class Node(object): # partially application specific
__slots__= 'next', 'NNd','PNd','key','data'

def __init__(self,Key,Data,P=None,N=None): # P = Previous N = Next
if P != None: P.NNd= self
if N != None: N.PNd= self
self.PNd= P
self.NNd= N
self.key= Key
self.data= Data

def _enqueue_after(self,Father):
self.NNd= Father.NNd
self.PNd= Father
Father.NNd= self

def _enqueue_before(self,Son):
self.NNd= Son
self.PNd= Son.PNd
Son.PNd= self

def _dequeue(self):
if self.PNd != None:
self.PNd.NNd= self.NNd
if self.NNd != None:
self.NNd.PNd= self.PNd
Father= self.PNd
Son= self.NNd
self.PNd= self.NNd= None
return Father,Son

def _next(self):
return self.NNd

def __str__(self): # highly application specific
return '(%s:%s)' % (self.key,self.data)

# some tests

MyQ= Queue()
MyQ.append( Node('HJ','3949') )

print MyQ.Head,MyQ.Tail,MyQ.Head.key
MyQ.append( Node('CJ','10149') )
print MyQ.Head,MyQ.Tail,MyQ.Head.key
N= MyQ.Head
print "queue: ",N.key,"->"

N= N.NNd
print "next: ",N.key,"->"
N= N.NNd
if N != None:
print "next: ",N.key,"->"
else:
print "no next:"

for N in MyQ:
print "loop->",N
print N.key,N.data

MyQ.dequeue(MyQ.Tail)
print "--- after dequeue"
print "Head: ",MyQ.Head," Tail: ",MyQ.Tail
for N in MyQ:
print "loop2->",N
print N.key,N.data

MyQ.dequeue(MyQ.Tail)
print "after second dequeue"
print "Head: ",MyQ.Head," Tail: ",MyQ.Tail

for N in MyQ:
print "loop3->",N
print N.key,N.data


ENJOY,
Helmut.

--
Helmut Jarausch

Lehrstuhl fuer Numerische Mathematik
RWTH - Aachen University
D 52056 Aachen, Germany

Steven D'Aprano

1/23/2008 11:55:00 AM

0

On Tue, 22 Jan 2008 22:59:07 -0800, bladedpenguin wrote:

> I am writing a game, and it must keep a list of objects. I've been
> representing this as a list, but I need an object to be able to remove
> itself. It doesn't know it's own index. If I tried to make each object
> keep track of it's own index, it would be invalidated when any object
> with a lower index was deleted. The error was that when I called
> list.remove(self), it just removed the first thing in hte list with the
> same type as what I wanted, rather than the object I wanted. The objects
> have no identifying charachteristics, other than thier location in
> memory
>
> So my question: How do I look something up in a list by it's location in
> memory? does python even support pointers?

That's the wrong question. It wouldn't even help you solve your problem.

Example:

alist = [object1, object2, object3, object4]

object3 is at location 0xb7d27df4 in memory. Now how do you delete it
from alist?


You say that list.remove(self) "just removed the first thing in the list
with the same type" -- that suggests a bug in your code, not in remove().
Perhaps "self" isn't what you think it is?

Here's how it should work:

class MyClass(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return "<%s>" % self.name
def delete_me(self, alist):
alist.remove(self)

obj1 = MyClass("Fred")
obj2 = MyClass("Betty")
obj3 = MyClass("Jill")
alist = [obj1, obj2, obj3]


Now let's see it in action:


>>> alist
[<Fred>, <Betty>, <Jill>]
>>> obj3.delete_me(alist) # tell obj3 to delete itself from the list
>>> alist
[<Fred>, <Betty>]


Works fine.

But what you can't do is create a NEW object, one that isn't in the list
but just looks similar, and expect it to be removed:

>>> MyClass("Fred").delete_me(alist) # a different "Fred".
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delete_me
ValueError: list.remove(x): x not in list


For that to work, you need to give your class an __eq__ method, and have
it match by name:

# put this in MyClass
def __eq__(self, other):
return self.name == self.other



Now any instance MyClass("Fred") is equal to any other MyClass("Fred"),
and remove will work



> Is there a better way?


You might also consider using a dictionary instead of a list.



--
Steven

NIm

1/23/2008 12:13:00 PM

0

On Jan 23, 2:24 am, Robert Kern <robert.k...@gmail.com> wrote:
> bladedpeng...@gmail.com wrote:
> > I am writing a game, and it must keep a list of objects. I've been
> > representing this as a list, but I need an object to be able to remove
> > itself. It doesn't know it's own index. If I tried to make each object
> > keep track of it's own index, it would be invalidated when any object
> > with a lower index was deleted. The error was that when I called
> > list.remove(self), it just removed the first thing in hte list with
> > the same type as what I wanted, rather than the object I wanted. The
> > objects have no identifying charachteristics, other than thier
> > location in memory
>
> By default, classes that do not implement the special methods __eq__ or __cmp__
> get compared by identity; i.e. "(x == y) == (x is y)". Double-check your classes
> and their super-classes for implementations of one of these methods.
> mylist.remove(x) will check "x is mylist[i]" first and only check "x ==
> mylist[i]" if that is False.
>
> In [1]: class A(object):
> ...: def __eq__(self, other):
> ...: print '%r == %r' % (self, other)
> ...: return self is other
> ...: def __ne__(self, other):
> ...: print '%r != %r' % (self, other)
> ...: return self is not other
> ...:
> ...:
>
> In [2]: As = [A() for i in range(10)]
>
> In [3]: As
> Out[3]:
> [<__main__.A object at 0xf47f70>,
> <__main__.A object at 0xf47d90>,
> <__main__.A object at 0xf47db0>,
> <__main__.A object at 0xf47cb0>,
> <__main__.A object at 0xf47eb0>,
> <__main__.A object at 0xf47e70>,
> <__main__.A object at 0xf47cd0>,
> <__main__.A object at 0xf47e10>,
> <__main__.A object at 0xf47dd0>,
> <__main__.A object at 0xf47e90>]
>
> In [4]: A0 = As[0]
>
> In [5]: A0
> Out[5]: <__main__.A object at 0xf47f70>
>
> In [6]: As.remove(A0)
>
> In [7]: As
> Out[7]:
> [<__main__.A object at 0xf47d90>,
> <__main__.A object at 0xf47db0>,
> <__main__.A object at 0xf47cb0>,
> <__main__.A object at 0xf47eb0>,
> <__main__.A object at 0xf47e70>,
> <__main__.A object at 0xf47cd0>,
> <__main__.A object at 0xf47e10>,
> <__main__.A object at 0xf47dd0>,
> <__main__.A object at 0xf47e90>]
>
> In [8]: A0
> Out[8]: <__main__.A object at 0xf47f70>
>
> In [9]: A9 = As[-1]
>
> In [10]: As.remove(A9)
> <__main__.A object at 0xf47d90> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47db0> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47cb0> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47eb0> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47e70> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47cd0> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47e10> == <__main__.A object at 0xf47e90>
> <__main__.A object at 0xf47dd0> == <__main__.A object at 0xf47e90>
>
> In [11]: As
> Out[11]:
> [<__main__.A object at 0xf47d90>,
> <__main__.A object at 0xf47db0>,
> <__main__.A object at 0xf47cb0>,
> <__main__.A object at 0xf47eb0>,
> <__main__.A object at 0xf47e70>,
> <__main__.A object at 0xf47cd0>,
> <__main__.A object at 0xf47e10>,
> <__main__.A object at 0xf47dd0>]
>
> In [12]: A9
> Out[12]: <__main__.A object at 0xf47e90>
>
> If you cannot find an implementation of __eq__ or __cmp__ anywhere in your code,
> please try to make a small, self-contained example like the one above but which
> demonstrates your problem.
>
> > So my question: How do I look something up in a list by it's location
> > in memory? does python even support pointers?
>
> If you need to keep an __eq__ that works by equality of value instead of
> identity, then you could keep a dictionary keyed by the id() of the object. That
> will correspond to its C pointer value in memory.
>
> In [13]: id(A9)
> Out[13]: 16023184
>
> In [14]: hex(_)
> Out[14]: '0xf47e90'
>
> > Is there a better way?
>
> Possibly. It looks like you are implementing a cache of some kind. Depending on
> exactly how you are using it, you might want to consider a "weak" dictionary
> instead. A weak dictionary, specifically a WeakValueDictionary, acts like a
> normal dictionary, but only holds a weak reference to the object. A weak
> reference does not increment the object's reference count like a normal
> ("strong") reference would. Consequently, once all of the "strong" references
> disappear, the object will be removed from the WeakValueDictionary without your
> having to do anything explicit. If this corresponds with when you want the
> object to be removed from the cache, then you might want to try this approach.
> Use "id(x)" as the key if there is no more meaningful key that fits your
> application.
>
> http://docs.python.org/lib/module-we...
>
> --
> Robert Kern
>
> "I have come to believe that the whole world is an enigma, a harmless enigma
> that is made terrible by our own mad attempt to interpret it as though it had
> an underlying truth."
> -- Umberto Eco

So, in general, is it more efficient to use a dictionary or to
override the __eq__ function?

eduardo.padoan@gmail.com

1/23/2008 12:33:00 PM

0

On Jan 23, 2008 9:55 AM, Steven D'Aprano
<steve@remove-this-cybersource.com.au> wrote:
> For that to work, you need to give your class an __eq__ method, and have
> it match by name:
>
> # put this in MyClass
> def __eq__(self, other):
> return self.name == self.other

Do you mean:

# put this in MyClass
def __eq__(self, other):
return self.name == other.name

?

--
http://www.advogato.org/person...
Bookmarks: http://del.icio....

Peter Otten

1/23/2008 1:10:00 PM

0

bladedpenguin wrote:

> So, in general, is it more efficient to use a dictionary or to override
> the __eq__ function?

Rule of thumb: If you want to add/remove arbitrary objects from a
collection a dictionary (or set) is always faster than a list.

You may still have to override the __eq__() and __hash__() methods whenever
you have distinct objects that can be equal. Caveat: don't use dictionaries
if the result of

obj1 == obj2 # items in the dict

can change during the lifetime of the collection.

Peter

Robert Kern

1/23/2008 7:45:00 PM

0

bladedpenguin@gmail.com wrote:

> So, in general, is it more efficient to use a dictionary or to
> override the __eq__ function?

Sorry, I guess I wasn't as clear as I could be. If your classes have not
overridden __eq__ or __cmp__, list.remove() should have worked just fine. If you
want your objects to be compared by identity (in list.remove(), as dictionary
keys, in explicit "x==y" expressions), do not implement __eq__.

Once you've resolved your __eq__ problems, then yes, you should consider a
dictionary or a set, particularly if your list gets large. By default, if your
classes have not overridden the __hash__ method, your objects will use identity
to distinguish themselves.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco