Python 对象自我删除

为什么这不能工作? 我正在尝试使一个类的实例删除本身。

>>> class A():
def kill(self):
del self




>>> a = A()
>>> a.kill()
>>> a
<__main__.A instance at 0x01F23170>
149304 次浏览

'self' is only a reference to the object. 'del self' is deleting the 'self' reference from the local namespace of the kill function, instead of the actual object.

To see this for yourself, look at what happens when these two functions are executed:

>>> class A():
...     def kill_a(self):
...         print self
...         del self
...     def kill_b(self):
...         del self
...         print self
...
>>> a = A()
>>> b = A()
>>> a.kill_a()
<__main__.A instance at 0xb771250c>
>>> b.kill_b()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in kill_b
UnboundLocalError: local variable 'self' referenced before assignment

You don't need to use del to delete instances in the first place. Once the last reference to an object is gone, the object will be garbage collected. Maybe you should tell us more about the full problem.

Indeed, Python does garbage collection through reference counting. As soon as the last reference to an object falls out of scope, it is deleted. In your example:

a = A()
a.kill()

I don't believe there's any way for variable 'a' to implicitly set itself to None.

I'm curious as to why you would want to do such a thing. Chances are, you should just let garbage collection do its job. In python, garbage collection is pretty deterministic. So you don't really have to worry as much about just leaving objects laying around in memory like you would in other languages (not to say that refcounting doesn't have disadvantages).

Although one thing that you should consider is a wrapper around any objects or resources you may get rid of later.

class foo(object):
def __init__(self):
self.some_big_object = some_resource


def killBigObject(self):
del some_big_object

In response to Null's addendum:

Unfortunately, I don't believe there's a way to do what you want to do the way you want to do it. Here's one way that you may wish to consider:

>>> class manager(object):
...     def __init__(self):
...             self.lookup = {}
...     def addItem(self, name, item):
...             self.lookup[name] = item
...             item.setLookup(self.lookup)
>>> class Item(object):
...     def __init__(self, name):
...             self.name = name
...     def setLookup(self, lookup):
...             self.lookup = lookup
...     def deleteSelf(self):
...             del self.lookup[self.name]
>>> man = manager()
>>> item = Item("foo")
>>> man.addItem("foo", item)
>>> man.lookup
{'foo': <__main__.Item object at 0x81b50>}
>>> item.deleteSelf()
>>> man.lookup
{}

It's a little bit messy, but that should give you the idea. Essentially, I don't think that tying an item's existence in the game to whether or not it's allocated in memory is a good idea. This is because the conditions for the item to be garbage collected are probably going to be different than what the conditions are for the item in the game. This way, you don't have to worry so much about that.

In this specific context, your example doesn't make a lot of sense.

When a Being picks up an Item, the item retains an individual existence. It doesn't disappear because it's been picked up. It still exists, but it's (a) in the same location as the Being, and (b) no longer eligible to be picked up. While it's had a state change, it still exists.

There is a two-way association between Being and Item. The Being has the Item in a collection. The Item is associated with a Being.

When an Item is picked up by a Being, two things have to happen.

  • The Being how adds the Item in some set of items. Your bag attribute, for example, could be such a set. [A list is a poor choice -- does order matter in the bag?]

  • The Item's location changes from where it used to be to the Being's location. There are probably two classes os Items - those with an independent sense of location (because they move around by themselves) and items that have to delegate location to the Being or Place where they're sitting.

Under no circumstances does any Python object ever need to get deleted. If an item is "destroyed", then it's not in a Being's bag. It's not in a location.

player.bag.remove(cat)

Is all that's required to let the cat out of the bag. Since the cat is not used anywhere else, it will both exist as "used" memory and not exist because nothing in your program can access it. It will quietly vanish from memory when some quantum event occurs and memory references are garbage collected.

On the other hand,

here.add( cat )
player.bag.remove(cat)

Will put the cat in the current location. The cat continues to exist, and will not be put out with the garbage.

I can't tell you how this is possible with classes, but functions can delete themselves.

def kill_self(exit_msg = 'killed'):
global kill_self
del kill_self
return exit_msg

And see the output:

 >>> kill_self
<function kill_self at 0x02A2C780>
>>> kill_self()
'killed'
>>> kill_self
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
kill_self
NameError: name 'kill_self' is not defined

I don't think that deleting an individual instance of a class without knowing the name of it is possible.

NOTE: If you assign another name to the function, the other name will still reference the old one, but will cause errors once you attempt to run it:

>>> x = kill_self
>>> kill_self()
>>> kill_self
NameError: name 'kill_self' is not defined
>>> x
<function kill_self at 0x...>
>>> x()
NameError: global name 'kill_self' is not defined

I am trying the same thing. I have a RPG battle system in which my Death(self) function has to kill the own object of the Fighter class. But it appeared it`s not possible. Maybe my class Game in which I collect all participants in the combat should delete units form the "fictional" map???

   def Death(self):
if self.stats["HP"] <= 0:
print("%s wounds were too much... Dead!"%(self.player["Name"]))
del self
else:
return True


def Damage(self, enemy):
todamage = self.stats["ATK"] + randint(1,6)
todamage -= enemy.stats["DEF"]
if todamage >=0:
enemy.stats["HP"] -= todamage
print("%s took %d damage from your attack!"%(enemy.player["Name"], todamage))
enemy.Death()
return True
else:
print("Ineffective...")
return True
def Attack(self, enemy):
tohit = self.stats["DEX"] + randint(1,6)
if tohit > enemy.stats["EVA"]:
print("You landed a successful attack on %s "%(enemy.player["Name"]))
self.Damage(enemy)
return True
else:
print("Miss!")
return True
def Action(self, enemylist):
for i in range(0, len(enemylist)):
print("No.%d, %r"%(i, enemylist[i]))
print("It`s your turn, %s. Take action!"%(self.player["Name"]))
choice = input("\n(A)ttack\n(D)efend\n(S)kill\n(I)tem\n(H)elp\n>")
if choice == 'a'or choice == 'A':
who = int(input("Who? "))
self.Attack(enemylist[who])
return True
else:
return self.Action()

Realistically you should not need to delete the object to do what you are trying to do. Instead you can change the state of the object. An example of how this works without getting into the coding would be your player fighting a monster and killing the monster. The state of this monster is fighting. The monster will be accessing all methods needed for fighting. When the monster dies because his health drops to 0, the monsters state will change to dead and your character will stop attacking automatically. This methodology is very similar to using flags or even keywords.

Also apparently in python deleting classes is not required since they will be garbage collected automatically when they are not used anymore.

If you're using a single reference to the object, then the object can kill itself by resetting that outside reference to itself, as in:

class Zero:
pOne = None


class One:


pTwo = None


def process(self):
self.pTwo = Two()
self.pTwo.dothing()
self.pTwo.kill()


# now this fails:
self.pTwo.dothing()




class Two:


def dothing(self):
print "two says: doing something"


def kill(self):
Zero.pOne.pTwo = None




def main():
Zero.pOne = One() # just a global
Zero.pOne.process()




if __name__=="__main__":
main()

You can of course do the logic control by checking for the object existence from outside the object (rather than object state), as for instance in:

if object_exists:
use_existing_obj()
else:
obj = Obj()

what you could do is take the name with you in the class and make a dictionairy:

class A:
def __init__(self, name):
self.name=name
def kill(self)
del dict[self.name]


dict={}
dict["a"]=A("a")
dict["a"].kill()

I think I've finally got it!
NOTE: You should not use this in normal code, but it is possible. This is only meant as a curiosity, see other answers for real-world solutions to this problem.


Take a look at this code:

# NOTE: This is Python 3 code, it should work with python 2, but I haven't tested it.
import weakref


class InsaneClass(object):
_alive = []
def __new__(cls):
self = super().__new__(cls)
InsaneClass._alive.append(self)


return weakref.proxy(self)


def commit_suicide(self):
self._alive.remove(self)


instance = InsaneClass()
instance.commit_suicide()
print(instance)


# Raises Error: ReferenceError: weakly-referenced object no longer exists

When the object is created in the __new__ method, the instance is replaced by a weak reference proxy and the only strong reference is kept in the _alive class attribute.

What is a weak-reference?

Weak-reference is a reference which does not count as a reference when the garbage collector collects the object. Consider this example:

>>> class Test(): pass


>>> a = Test()
>>> b = Test()


>>> c = a
>>> d = weakref.proxy(b)
>>> d
<weakproxy at 0x10671ae58 to Test at 0x10670f4e0>
# The weak reference points to the Test() object


>>> del a
>>> c
<__main__.Test object at 0x10670f390> # c still exists


>>> del b
>>> d
<weakproxy at 0x10671ab38 to NoneType at 0x1002050d0>
# d is now only a weak-reference to None. The Test() instance was garbage-collected

So the only strong reference to the instance is stored in the _alive class attribute. And when the commit_suicide() method removes the reference the instance is garbage-collected.

class A:
def __init__(self, function):
self.function = function
def kill(self):
self.function(self)


def delete(object):                        #We are no longer in A object
del object


a = A(delete)
print(a)
a.kill()
print(a)

May this code work ?

This is something I have done in the past. Create a list of objects, and you can then have objects delete themselves with the list.remove() method.

bullet_list = []


class Bullet:
def kill_self(self):
bullet_list.remove(self)


bullet_list += [Bullet()]

A replacement implement:

class A:


def __init__(self):
self.a = 123


def kill(self):
from itertools import chain
for attr_name in chain(dir(self.__class__), dir(self)):
if attr_name.startswith('__'):
continue
attr = getattr(self, attr_name)
if callable(attr):
setattr(self, attr_name, lambda *args, **kwargs: print('NoneType'))
else:
setattr(self, attr_name, None)
a.__str__ = lambda: ''
a.__repr__ = lambda: ''
a = A()
print(a.a)
a.kill()


print(a.a)
a.kill()


a = A()
print(a.a)

will outputs:

123
None
NoneType
123

EDIT: This does not work

If you create an object with an attribute corresponding to the name of the variable it is technically possible using exec.

class Example():
def __init__(self, name) -> None:
self.var_name = name


def kill(self):
exec(f'del {self.var_name}')


coolvar = Example('coolvar')
coolvar.kill()

Keep in mind that you should avoid using exec at all costs, always.