90

Why won't this work? I'm trying to make an instance of a class delete itself.

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


>>> a = A()
>>> a.kill()
>>> a
<__main__.A instance at 0x01F23170>
Sam
  • 7,252
  • 16
  • 46
  • 65

15 Answers15

76

'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
Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
  • 21
    Is there a way to design a method which make an instance delete itself, or call a global function to delete it? – Zen Jul 20 '14 at 04:33
48

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.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 1
    Is there a way to design a method which make an instance delete itself, or call a global function to delete it? – Zen Jul 20 '14 at 04:34
  • 3
    @Zen there is no way in Python to delete an instance. All you can do is delete references to the instance, and once they are all gone, the object is reclaimed. – Ned Batchelder Jul 20 '14 at 12:22
  • 1
    Is there a clue to delete all these references? – Zen Jul 21 '14 at 02:58
  • 3
    @Zen you have to understand your program well enough to know what is referring to your object. Why are you trying to delete an object? Why do you care, it will go away on its own when it isn't being used any more. – Ned Batchelder Jul 21 '14 at 11:12
43

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.

skywalker
  • 1,230
  • 14
  • 30
14

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.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 3
    Schrodinger's cat lives! Or doesn't! That pesky Copenhagen Interpretation is so confusing. So's garbage collection. – S.Lott Nov 17 '08 at 14:57
  • does it make sense ? - is an religious question, what is happening ? - is the scientific question! – Blauohr Nov 23 '08 at 16:31
  • @Blauohr: Since I believe software must capture meaning, I'm making a religious judgement. – S.Lott Nov 23 '08 at 19:12
  • @Blauohr: Since I can't see a useful distinction between what is meant and what happens, If what happens is not what it means, then there's a bug, and we need to make what happens match what was meant. Deep analysis of what happens -- when that's a bug -- isn't helpful. – S.Lott Nov 24 '08 at 13:46
7

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.

Zachary Kraus
  • 1,051
  • 10
  • 21
4

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
3

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()
Ilian Zapryanov
  • 1,132
  • 2
  • 16
  • 28
  • 2
    The code example is verbose but conceptually the idea of "removing from the fictional map" is a good way to explain removing a reference from a given scope. – Dave Rawks Mar 07 '12 at 00:45
2

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.

Cybis
  • 9,773
  • 2
  • 36
  • 37
2

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()]
Mihai Chelaru
  • 7,614
  • 14
  • 45
  • 51
Henry
  • 21
  • 1
1

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()
Basel Shishani
  • 7,735
  • 6
  • 50
  • 67
0

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()
Theoxis
  • 1
  • 1
0

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.

Community
  • 1
  • 1
Jason Baker
  • 192,085
  • 135
  • 376
  • 510
0
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 ?

  • Welcome to Stack Overflow! Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation would greatly improve its long-term value](//meta.stackexchange.com/q/114762/206345) by showing _why_ this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Blue Oct 29 '18 at 22:18
0

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
Sailist
  • 134
  • 8
0

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.

montw
  • 85
  • 10