Python объект удаляет себя - PullRequest
56 голосов
/ 16 ноября 2008

Почему это не сработает? Я пытаюсь сделать экземпляр класса удалить себя.

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


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

Ответы [ 13 ]

66 голосов
/ 16 ноября 2008

«Я» - это только ссылка на объект. «del self» удаляет ссылку «self» из локального пространства имен функции kill вместо реального объекта.

Чтобы убедиться в этом, посмотрите, что происходит при выполнении этих двух функций:

>>> 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
40 голосов
/ 16 ноября 2008

Вам не нужно использовать del для удаления экземпляров. Как только последняя ссылка на объект исчезнет, ​​этот объект будет удален. Может быть, вы должны рассказать нам больше о полной проблеме.

13 голосов
/ 16 ноября 2008

В этом конкретном контексте ваш пример не имеет большого смысла.

Когда Существо поднимает Предмет, предмет сохраняет индивидуальное существование. Он не исчезает, потому что его подобрали. Он все еще существует, но он (а) находится в том же месте, что и Существо, и (б) больше не имеет права быть подобранным. Несмотря на изменение состояния, оно все еще существует.

Существует двусторонняя связь между Бытием и Предметом. Существо имеет Предмет в коллекции. Предмет связан с Существом.

Когда Предмет берется Существом, должны произойти две вещи.

  • Существо, как добавляет Предмет в некоторые set предметов. Например, ваш атрибут bag может быть таким set. [A list плохой выбор - имеет ли значение заказ в сумке?]

  • Местоположение Предмета меняется с того места, где оно было раньше, на местоположение Существа. Вероятно, есть два класса предметов - предметы с независимым ощущением местоположения (потому что они перемещаются сами по себе) и предметы, которые должны делегировать местоположение существу или месту, где они сидят.

Ни при каких обстоятельствах ни один объект Python никогда не должен быть удален. Если предмет «уничтожен», значит, его нет в сумке Существа. Это не в месте.

player.bag.remove(cat)

Это все, что требуется, чтобы выпустить кота из сумки. Поскольку кошка больше нигде не используется, она будет существовать как «используемая» память и не будет существовать, потому что ничто в вашей программе не может получить к ней доступ. Он тихо исчезнет из памяти, когда произойдет какое-то квантовое событие, и ссылки на память будут удалены.

С другой стороны,

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

Поместит кота в текущее местоположение. Кошка продолжает существовать и не будет выброшена вместе с мусором.

10 голосов
/ 13 августа 2016

Я думаю, что наконец-то понял!
ПРИМЕЧАНИЕ: Вы не должны использовать это в нормальном коде , но это возможно . Это только для любопытства, см. Другие ответы для реальных решений этой проблемы. <ч /> Посмотрите на этот код:

# 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

Когда объект создается в методе __new__, экземпляр заменяется прокси-сервером со слабой ссылкой, и в атрибуте _alive class сохраняется только сильная ссылка.

Что такое слабая ссылка?

Слабая ссылка - это ссылка, которая не считается ссылкой, когда сборщик мусора собирает объект. Рассмотрим этот пример:

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

Таким образом, единственная надежная ссылка на экземпляр хранится в атрибуте класса _alive. И когда метод commit_suicide () удаляет ссылку, экземпляр собирается для сбора мусора.

4 голосов
/ 28 сентября 2012

Реально вам не нужно удалять объект, чтобы делать то, что вы пытаетесь сделать. Вместо этого вы можете изменить состояние объекта. Примером того, как это работает, не вдаваясь в кодирование, будет ваш игрок, сражающийся с монстром и убивающий монстра. Состояние этого монстра сражается. Монстр получит доступ ко всем методам, необходимым для боя. Когда монстр умирает из-за того, что его здоровье падает до 0, состояние монстров изменится на мертвое, и ваш персонаж перестанет атаковать автоматически. Эта методология очень похожа на использование флагов или даже ключевых слов.

Также очевидно, что в python удаление классов не требуется, так как они будут собираться автоматически, когда они больше не используются.

3 голосов
/ 08 декабря 2011

Я пробую то же самое. У меня есть боевая система РПГ, в которой моя Смертная (само) функция должна убивать собственный объект класса Fighter. Но оказалось, что это невозможно. Может быть, в моем классе Game, в котором я собираю всех участников боя, следует удалить юниты с «вымышленной» карты ???

   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 голосов
/ 08 августа 2010

Я не могу сказать вам, как это возможно с классами, но функции могут удалить себя.

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

И посмотрите вывод:

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

Я не думаю, что удаление отдельного экземпляра класса без знания его имени возможно.

Примечание: Если вы назначите другое имя функции, другое имя все равно будет ссылаться на старое, но при попытке его запуска возникнут ошибки:

>>> 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
1 голос
/ 01 февраля 2013

Если вы используете одну ссылку на объект, то объект может убить себя, сбросив эту внешнюю ссылку на себя, как в:

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

Конечно, вы можете осуществлять логическое управление, проверяя существование объекта вне объекта (а не состояния объекта), как, например, в:

if object_exists:
   use_existing_obj()
else: 
   obj = Obj()
1 голос
/ 16 ноября 2008

Действительно, Python выполняет сборку мусора путем подсчета ссылок. Как только последняя ссылка на объект выходит из области видимости, он удаляется. В вашем примере:

a = A()
a.kill()

Я не верю, что у переменной 'a' есть возможность неявно установить себе значение None.

0 голосов
/ 18 ноября 2018

Это то, что я делал в прошлом. Создайте список объектов, и затем вы сможете удалить объекты самостоятельно с помощью метода list.remove().

bullet_list = []

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

bullet_list += [Bullet()]
...