なぜこれがうまくいかないのですか?クラスのインスタンス自体を削除しようとしています。
>>> class A():
def kill(self):
del self
>>> a = A()
>>> a.kill()
>>> a
<__main__.A instance at 0x01F23170>
なぜこれがうまくいかないのですか?クラスのインスタンス自体を削除しようとしています。
>>> class A():
def kill(self):
del self
>>> a = A()
>>> a.kill()
>>> a
<__main__.A instance at 0x01F23170>
「self」は、オブジェクトへの参照のみです。「del self」は、実際のオブジェクトではなく、kill 関数のローカル名前空間から「self」参照を削除しています。
これを自分で確認するには、次の 2 つの関数を実行したときに何が起こるかを見てください。
>>> 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
そもそもインスタンスを削除するために del を使用する必要はありません。オブジェクトへの最後の参照がなくなると、オブジェクトはガベージ コレクションされます。たぶん、あなたは私たちに完全な問題についてもっと教えてください.
やっと手に入ったと思います!
注: 通常のコードではこれを使用しないでください。ただし、可能です。これは単なる好奇心です。この問題に対する実際の解決策については、他の回答を参照してください。
# 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 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() メソッドが参照を削除すると、インスタンスはガベージ コレクションされます。
この特定のコンテキストでは、あなたの例はあまり意味がありません。
存在がアイテムを拾うと、そのアイテムは個別の存在を保持します。拾ったから消えない。それはまだ存在しますが、(a) 存在と同じ場所にあり、(b) 拾う資格がありません。状態が変化しても、まだ存在しています。
ビーイングとアイテムの間には双方向の関連があります。存在はコレクションにアイテムを持っています。アイテムはビーイングに関連付けられています。
アイテムが存在によって拾われるとき、2 つのことが起こらなければなりません。
ビーイングは、いくつかset
のアイテムにアイテムをどのように追加しますか。たとえばbag
、属性はset
. [Alist
は悪い選択です -- バッグの順番は重要ですか?]
アイテムの場所は、以前の場所からビーイングの場所に変わります。アイテムにはおそらく 2 つのクラスがあります。独立した位置感覚を持つもの (自分で動き回るため) と、位置を存在または座っている場所に委譲する必要があるアイテムです。
どのような状況でも、Python オブジェクトを削除する必要はありません。アイテムが「破壊」された場合、それはビーイングのバッグには入っていません。場所にはありません。
player.bag.remove(cat)
猫を袋から出すために必要なのはそれだけです。cat は他のどこでも使用されていないため、「使用済み」メモリとして存在し、プログラム内の何もアクセスできないため存在しません。量子イベントが発生し、メモリ参照がガベージ コレクションされると、メモリから静かに消えます。
一方で、
here.add( cat )
player.bag.remove(cat)
猫を現在の場所に置きます。猫は生き続け、ゴミに出すことはありません。
現実的には、実行しようとしていることを実行するためにオブジェクトを削除する必要はありません。代わりに、オブジェクトの状態を変更できます。コーディングに入らずにこれがどのように機能するかの例は、プレイヤーがモンスターと戦い、モンスターを殺すことです。このモンスターの状態は戦っています。モンスターは戦闘に必要なすべての方法にアクセスします。体力が0になったためにモンスターが死ぬと、モンスターの状態が死んでしまい、キャラクターの攻撃が自動的に停止します。この方法は、フラグやキーワードの使用と非常によく似ています。
また、Pythonでは、クラスが使用されなくなったときに自動的にガベージコレクションされるため、クラスを削除する必要がないようです。
私は同じことを試みています。私のDeath(self)関数がFighterクラスの自身のオブジェクトを殺さなければならないRPGバトルシステムがあります。しかし、それは不可能であるように見えました。たぶん、戦闘のすべての参加者を集める私のクラスのゲームは、「架空の」マップからユニットを削除する必要がありますか?
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()
クラスでこれがどのように可能かはわかりませんが、関数はそれ自体を削除できます。
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
実際、Python は参照カウントによってガベージ コレクションを行います。オブジェクトへの最後の参照が範囲外になるとすぐに削除されます。あなたの例では:
a = A()
a.kill()
変数 'a' を暗黙的に None に設定する方法はないと思います。
オブジェクトへの単一の参照を使用している場合、オブジェクトは、次のように、それ自体への外部参照をリセットすることで、それ自体を強制終了できます。
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()
交換用器具:
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)
出力します:
123
None
NoneType
123
あなたができることは、クラスであなたと一緒に名前を取り、辞書を作ることです:
class A:
def __init__(self, name):
self.name=name
def kill(self)
del dict[self.name]
dict={}
dict["a"]=A("a")
dict["a"].kill()
なぜそんなことをしたいのか、興味があります。おそらく、ガベージ コレクションに任せるべきでしょう。Python では、ガベージ コレクションはかなり決定論的です。したがって、他の言語の場合のように、オブジェクトをメモリ内にそのままにしておくことについてそれほど心配する必要はありません (refcounting に欠点がないことは言うまでもありません)。
ただし、考慮すべきことの 1 つは、後で削除する可能性のあるオブジェクトまたはリソースのラッパーです。
class foo(object):
def __init__(self):
self.some_big_object = some_resource
def killBigObject(self):
del some_big_object
Null の補遺への対応:
残念ながら、やりたいことをやりたいようにやる方法はないと思います。検討したい方法の 1 つを次に示します。
>>> 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
{}
少し面倒ですが、それでアイデアが得られるはずです。基本的に、ゲーム内でのアイテムの存在を、メモリに割り当てられているかどうかに結び付けるのは良い考えではないと思います。これは、ガベージ コレクションされるアイテムの条件が、ゲーム内のアイテムの条件と異なる可能性があるためです。このように、それほど心配する必要はありません。