プログラムの終わりに向かって、クラスのすべてのインスタンスから特定の変数を辞書にロードしようとしています。
例えば:
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):A.instancesのインスタンスのinstance.foo}リストは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>]
metaclass を使用してこの問題を解決することもできます。
__init__
メタクラスのメソッド)、新しいインスタンスレジストリを追加します__call__
メタクラスのメソッド)、インスタンスレジストリに追加します。このアプローチの利点は、インスタンスが存在しない場合でも、各クラスにレジストリがあることです。対照的に、__new__
( Blckknghtの回答 のように)、最初のインスタンスが作成されるときにレジストリが追加されます。
class MetaInstanceRegistry(type):
"""Metaclass providing an instance registry"""
def __init__(cls, name, bases, attrs):
# Create class
super(MetaInstanceRegistry, cls).__init__(name, bases, attrs)
# Initialize fresh instance storage
cls._instances = weakref.WeakSet()
def __call__(cls, *args, **kwargs):
# Create instance (calls __init__ and __new__ methods)
inst = super(MetaInstanceRegistry, cls).__call__(*args, **kwargs)
# Store weak reference to instance. WeakSet will automatically remove
# references to objects that have been garbage collected
cls._instances.add(inst)
return inst
def _get_instances(cls, recursive=False):
"""Get all instances of this class in the registry. If recursive=True
search subclasses recursively"""
instances = list(cls._instances)
if recursive:
for Child in cls.__subclasses__():
instances += Child._get_instances(recursive=recursive)
# Remove duplicates from multiple inheritance.
return list(set(instances))
使用法:レジストリを作成し、サブクラス化します。
class Registry(object):
__metaclass__ = MetaInstanceRegistry
class Base(Registry):
def __init__(self, x):
self.x = x
class A(Base):
pass
class B(Base):
pass
class C(B):
pass
a = A(x=1)
a2 = A(2)
b = B(x=3)
c = C(4)
for cls in [Base, A, B, C]:
print cls.__name__
print cls._get_instances()
print cls._get_instances(recursive=True)
print
del c
print C._get_instances()
abc
モジュールの抽象基本クラスを使用する場合は、サブクラスabc.ABCMeta
メタクラスの競合を回避するには:
from abc import ABCMeta, abstractmethod
class ABCMetaInstanceRegistry(MetaInstanceRegistry, ABCMeta):
pass
class ABCRegistry(object):
__metaclass__ = ABCMetaInstanceRegistry
class ABCBase(ABCRegistry):
__metaclass__ = ABCMeta
@abstractmethod
def f(self):
pass
class E(ABCBase):
def __init__(self, x):
self.x = x
def f(self):
return self.x
e = E(x=5)
print E._get_instances()
低レベルのハッキングとデバッグをすばやく行うためのもう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