プログラムの終わりに向かって、クラスのすべてのインスタンスから特定の変数を辞書にロードしようとしています。
例えば:
class Foo():
__init__(self):
x = {}
foo1 = Foo()
foo2 = Foo()
foo...etc.
インスタンスの数が変化し、Foo()の各インスタンスからのxdictを新しいdictにロードしたいとします。どうすればいいですか?
私がSOで見た例は、インスタンスのリストがすでにあることを前提としています。
プログラムの終わりに向かって、クラスのすべてのインスタンスから特定の変数を辞書にロードしようとしています。
例えば:
class Foo():
__init__(self):
x = {}
foo1 = Foo()
foo2 = Foo()
foo...etc.
インスタンスの数が変化し、Foo()の各インスタンスからのxdictを新しいdictにロードしたいとします。どうすればいいですか?
私がSOで見た例は、インスタンスのリストがすでにあることを前提としています。
インスタンスを追跡する 1 つの方法は、クラス変数を使用することです。
class A(object):
instances = []
def __init__(self, foo):
self.foo = foo
A.instances.append(self)
プログラムの最後に、次のように dict を作成できます。
foo_vars = {id(instance): instance.foo for instance in A.instances}
リストは 1 つだけです。
>>> a = A(1)
>>> b = A(2)
>>> A.instances
[<__main__.A object at 0x1004d44d0>, <__main__.A object at 0x1004d4510>]
>>> id(A.instances)
4299683456
>>> id(a.instances)
4299683456
>>> id(b.instances)
4299683456
@JoelCornettの答えは基本を完全にカバーしています。これは少し複雑なバージョンであり、いくつかの微妙な問題に役立つ可能性があります。
特定のクラスのすべての「ライブ」インスタンスにアクセスできるようにする場合は、以下をサブクラス化します(または、独自の基本クラスに同等のコードを含めます)。
from weakref import WeakSet
class base(object):
def __new__(cls, *args, **kwargs):
instance = object.__new__(cls, *args, **kwargs)
if "instances" not in cls.__dict__:
cls.instances = WeakSet()
cls.instances.add(instance)
return instance
これは、@JoelCornettが提示したより単純な実装で発生する可能性のある2つの問題に対処します。
の各サブクラスはbase、独自のインスタンスを個別に追跡します。親クラスのインスタンスリストにサブクラスインスタンスを取得することはなく、1つのサブクラスが兄弟サブクラスのインスタンスに遭遇することはありません。ユースケースによっては、これは望ましくない場合がありますが、セットを分割するよりも、セットをマージして戻す方がおそらく簡単です。
instancesセットはクラスのインスタンスへの弱参照を使用するため、コード内の他の場所のインスタンスに他のすべての参照を再割り当てした場合でも、del簿記コードによってガベージコレクションが妨げられることはありません。繰り返しになりますが、これは一部のユースケースでは望ましくない場合がありますが、すべてのインスタンスを本当に永久に存続させたい場合は、ウィークセットの代わりに通常のセット(またはリスト)を使用するのは簡単です。
いくつかの便利でダンディなテスト出力(セットは、うまく印刷されないという理由だけでinstances常に渡されます):list
>>> b = base()
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> class foo(base):
... pass
...
>>> f = foo()
>>> list(foo.instances)
[<__main__.foo object at 0x0000000002606898>]
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> del f
>>> list(foo.instances)
[]
インスタンスへの弱い参照を使用したいと思うでしょう。そうしないと、削除されたはずのインスタンスをクラスが追跡してしまう可能性があります。weakref.WeakSet は、デッド インスタンスをそのセットから自動的に削除します。
インスタンスを追跡する 1 つの方法は、クラス変数を使用することです。
import weakref
class A(object):
instances = weakref.WeakSet()
def __init__(self, foo):
self.foo = foo
A.instances.add(self)
@classmethod
def get_instances(cls):
return list(A.instances) #Returns list of all current instances
プログラムの最後に、次のように dict を作成できます。
foo_vars = {id(instance): instance.foo for instance in A.instances} リストは 1 つだけです。
>>> a = A(1)
>>> b = A(2)
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x100587250>]
>>> id(A.instances)
4299861712
>>> id(a.instances)
4299861712
>>> id(b.instances)
4299861712
>>> a = A(3) #original a will be dereferenced and replaced with new instance
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x1005872d0>]
低レベルのハッキングとデバッグをすばやく行うためのもう1つのオプションは、によって返されるオブジェクトのリストをフィルタリングし、gc.get_objects()その方法で辞書をその場で生成することです。CPythonでは、その関数はガベージコレクターが知っているすべての(通常は巨大な)リストを返すため、特定のユーザー定義クラスのすべてのインスタンスが確実に含まれます。
これはインタプリタの内部を少し掘り下げているので、Jython、PyPy、IronPythonなどで動作する(または動作しない)場合があることに注意してください。私はチェックしていません。それにもかかわらず、それは本当に遅い可能性もあります。注意して使用してください/YMMVなど。
ただし、この質問に遭遇した人の中には、奇妙な動作をしているコードのスライスの実行時の状態で何が起こっているのかを理解するために、最終的にこの種のことを1回限りで実行したいと思う人もいると思います。この方法には、インスタンスやその構築にまったく影響を与えないという利点があります。これは、問題のコードがサードパーティのライブラリなどからのものである場合に役立つ可能性があります。
@Joel Cornettからの回答を使用して、私は次のことを思いつきました。これはうまくいくようです。つまり、オブジェクト変数を合計することができます。
import os
os.system("clear")
class Foo():
instances = []
def __init__(self):
Foo.instances.append(self)
self.x = 5
class Bar():
def __init__(self):
pass
def testy(self):
self.foo1 = Foo()
self.foo2 = Foo()
self.foo3 = Foo()
foo = Foo()
print Foo.instances
bar = Bar()
bar.testy()
print Foo.instances
x_tot = 0
for inst in Foo.instances:
x_tot += inst.x
print x_tot
出力:
[<__main__.Foo instance at 0x108e334d0>]
[<__main__.Foo instance at 0x108e334d0>, <__main__.Foo instance at 0x108e33560>, <__main__.Foo instance at 0x108e335a8>, <__main__.Foo instance at 0x108e335f0>]
5
10
15
20