web-dev-qa-db-fra.com

Objet Python se supprimant

Pourquoi ça ne marche pas? J'essaye de faire en sorte qu'une instance d'une classe se supprime elle-même.

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


>>> a = A()
>>> a.kill()
>>> a
<__main__.A instance at 0x01F23170>
56
Null

'self' n'est qu'une référence à l'objet. 'del self' supprime la référence 'self' de l'espace de noms local de la fonction kill, au lieu de l'objet réel.

Pour voir cela par vous-même, regardez ce qui se passe lorsque ces deux fonctions sont exécutées:

>>> 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
66
Jeremy Ruten

Vous n'avez pas besoin d'utiliser del pour supprimer des instances en premier lieu. Une fois que la dernière référence à un objet est partie, l'objet est récupéré. Peut-être devriez-vous nous en dire plus sur l'ensemble du problème.

40
Ned Batchelder

Dans ce contexte spécifique, votre exemple n’a pas beaucoup de sens.

Lorsqu'un être prend un objet, cet objet conserve une existence individuelle. Ça ne disparaît pas parce que c'est ramassé. Il existe toujours, mais il est (a) au même endroit que l'Être et (b) n'est plus éligible pour être pris en charge. Bien qu'il y ait eu un changement d'état, il existe toujours.

Il existe une association à double sens entre Être et Item. L'être a l'élément dans une collection. L'élément est associé à un être.

Quand un être est ramassé par un être, deux choses doivent se passer.

  • The Being how ajoute l’élément dans une set d’éléments. Votre attribut bag, par exemple, pourrait être un tel set. [Une list est un mauvais choix - est-ce que l'ordre compte dans le sac?]

  • L'emplacement de l'objet change de l'endroit où il se trouvait à l'emplacement de l'être. Il existe probablement deux classes d'objets: ceux qui ont une idée indépendante de l'emplacement (parce qu'ils se déplacent d'eux-mêmes) et ceux qui doivent déléguer l'emplacement à l'être ou à l'endroit où ils sont assis.

En aucun cas, aucun objet Python n'a besoin d'être supprimé. Si un objet est "détruit", il n'est pas dans le sac d'un être. Ce n'est pas dans un endroit. 

player.bag.remove(cat)

Est-ce tout ce qui est nécessaire pour laisser le chat sortir du sac? Etant donné que le chat n'est utilisé nulle part ailleurs, il existera à la fois comme mémoire "utilisée" et non car aucun programme de votre programme ne pourra y accéder. Il disparaîtra doucement de la mémoire quand un événement quantique se produira et que les références de la mémoire seront ramassées.

D'autre part,

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

Va mettre le chat à l'emplacement actuel. Le chat continue d'exister et ne sera pas mis à la poubelle.

13
S.Lott

Je pense que j'ai enfin compris!
REMARQUE: vous ne devriez pas utiliser ceci dans le code normal , mais c'est possible . Cela est uniquement destiné à être une curiosité, voir les autres réponses pour des solutions concrètes à ce problème.


Regardez ce 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

Lorsque l'objet est créé dans la méthode __new__, l'instance est remplacée par un proxy de référence faible et seule la référence forte est conservée dans l'attribut de classe _alive.

Qu'est-ce qu'une référence faible?

Référence faible est une référence qui ne compte pas comme référence lorsque le garbage collector collecte l'objet. Considérons cet exemple:

>>> 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

Ainsi, la seule référence forte à l'instance est stockée dans l'attribut de classe _alive. Et lorsque la méthode commit_suicide () supprime la référence, l'instance est récupérée.

8
les

De manière réaliste, vous ne devriez pas avoir besoin de supprimer l'objet pour faire ce que vous essayez de faire. Au lieu de cela, vous pouvez changer l'état de l'objet. Un exemple de la façon dont cela fonctionne sans entrer dans le codage serait votre joueur combattant un monstre et le tuant. L'état de ce monstre se bat. Le monstre aura accès à toutes les méthodes nécessaires au combat. Lorsque le monstre meurt parce que son état de santé tombe à 0, son état redevient mort et votre personnage cesse d'attaquer automatiquement. Cette méthodologie est très similaire à l'utilisation d'indicateurs ou même de mots-clés.

De plus, apparemment, en python, la suppression de classes n'est pas nécessaire, car elles seront automatiquement récupérées lorsqu'elles ne seront plus utilisées. 

4
Zachary Kraus

J'essaie la même chose. J'ai un système de combat RPG dans lequel ma fonction Death (self) doit tuer le propre objet de la classe Fighter. Mais il est apparu que ce n’est pas possible. Peut-être que ma classe de jeu dans laquelle je rassemble tous les participants au combat devrait supprimer des unités de la carte "fictive" ???

   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()
3
Ilian Zapryanov

Je ne peux pas vous dire comment cela est possible avec des classes, mais les fonctions peuvent se supprimer elles-mêmes. 

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

Et voir la sortie:

 >>> 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

Je ne pense pas que supprimer une instance individuelle d'une classe sans en connaître le nom soit possible. 

REMARQUE: Si vous attribuez un autre nom à la fonction, ce dernier fera toujours référence à l'ancien, mais provoquera des erreurs une fois que vous aurez tenté de l'exécuter:

>>> 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
2
Nathanael Abbotts

Si vous utilisez une seule référence à l'objet, celui-ci peut se tuer en réinitialisant cette référence externe sur lui-même, comme dans:

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()

Vous pouvez bien sûr effectuer le contrôle logique en recherchant l'existence de l'objet de l'extérieur de l'objet (plutôt que de l'état de l'objet), comme par exemple dans:

if object_exists:
   use_existing_obj()
else: 
   obj = Obj()
1
Basel Shishani

En effet, Python effectue la récupération de place par comptage de références. Dès que la dernière référence à un objet tombe en dehors de la portée, elle est supprimée. Dans votre exemple:

a = A()
a.kill()

Je ne crois pas qu'il soit possible pour la variable 'a' de se définir implicitement à None.

1
Cybis

Je suis curieux de savoir pourquoi vous voudriez faire une telle chose. Les chances sont, vous devriez simplement laisser la collecte des ordures faire son travail. En python, la récupération de place est assez déterministe. Ainsi, vous n'avez pas vraiment à vous soucier de laisser des objets en mémoire, comme dans d'autres langues (pour ne pas dire que recomptage n'a pas d'inconvénients).

Bien que l’une des choses que vous devriez considérer est un wrapper autour d’objets ou de ressources dont vous pourriez vous débarrasser plus tard.

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

    def killBigObject(self):
        del some_big_object

En réponse à Addendum de Null :

Malheureusement, je ne crois pas qu'il existe un moyen de faire ce que vous voulez faire comme vous voulez. Voici une façon que vous voudrez peut-être envisager:

>>> 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
 {}

C'est un peu brouillon, mais ça devrait vous donner une idée. Pour l’essentiel, je ne pense pas que lier l’existence d’un élément dans le jeu à son affectation en mémoire est une bonne idée. En effet, les conditions pour que l'objet soit ramassé seront probablement différentes de ce qu'elles sont pour l'élément dans le jeu. De cette façon, vous n'avez pas à vous inquiéter autant à ce sujet.

0
Jason Baker
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)

Ce code peut-il fonctionner?

0
Anonyme

ce que vous pouvez faire, c'est prendre le nom avec vous dans la classe et créer un dictionnaire:

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

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

C’est quelque chose que j’ai fait par le passé ..___ Créez une liste d’objets et vous pourrez ensuite les faire supprimer avec la méthode list.remove().

bullet_list = []

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

bullet_list += [Bullet()]
0
Henry