1

このクラスから既に作成されているインスタンスのリストに反復子を返す iterable クラスを作成するタスクがあります。次に例を示します

x = SomeClass()
y = SomeClass()
for obj in SomeClass:
    print obj
>><__main__.SomeClass object at .......> and etc

metaclass と globals() を介して作成しました。それはひどいように見えますが、うまくいきます。そして、よりエレガントでpythonicな方法を見つけたいです。最初はweakrefを介してこれを作成したいのですが、メタクラスから作業クラスからいくつかの変数を取得する方法がわかりません.メタクラスのオブジェクトに参照を保存しようとすると、私のメタクラスへの参照が作成されます.クラスなので、これが私の実装です:

class ObjectsInMemory(object):

    class __metaclass__(type):
       def __init__(self, *args, **kwargs):
            self.name = args[0]
       def __iter__(self):
            glob = globals().copy()
            cls_inst = []
            for _, v in glob.iteritems():
                try:
                    if self.name in v.__repr__():
                        cls_inst.append(v)
                except TypeError:
                    pass
            yield cls_inst
4

3 に答える 3

3

一部のクラスのインスタンスを追跡したい場合は、 のような弱参照コンテナーを使用することをお勧めしweakref.WeakSetます。このようなコンテナ クラスのコード スケッチ:

import weakref

class InstanceTracker(object):
    def __init__(self):
        self.instances = {}
    def new(self, cls, *args, **kwargs):
        instance = cls(*args, **kwargs)
        self.instances.setdefault(cls, weakref.WeakSet()).add(instance)
        return instance
    def iter_instances(cls):
        return iter(self.instances[cls])

SomeClassインスタンスに対してwithの新しいインスタンスを作成できるようになりtracker.new(SomeClass)ました。InstanceTrackertracker

この方法では、グローバルな状態に依存する必要がなく、関数に奇妙な副作用がなく、インスタンスを追跡するためにクラスを変更する必要がありません。

于 2012-08-09T17:15:37.987 に答える
2

正直なところ、求めている機能に対するアプローチが間違っていると思います。

まず第一に、グローバルは悪いものです。できれば避けてください。次に、グローバル イテラブルを作成するためにメタクラスを作成する必要はありません。代わりに、次のことを試してください。

class SomeClass():
    ALL_INSTANCES = set()
    def __init__(self):
        self.ALL_INSTANCES.add(self)

for obj in SomeClass.ALL_INSTANCES:
    print obj

これにより、メタクラスの恐ろしい混乱なしに、同じ反復可能な機能が得られます。また、グローバル定義が SomeClass スコープ内に隠されるため、グローバルの心配が少なくなります (ただし、すべてではありませんが、グローバルな状態が維持されるため)。

__del__Class タイプが存在する限り、本質的にオブジェクトへの参照を持っているため、自動的に呼び出されないため、ALL_INSTANCES 内のオブジェクトを簡単に削除できないことに注意してください。defined を手動でdel呼び出すと__del__、グローバル セットからオブジェクトが削除されます (循環参照がない限り、最後の参照がなくなると)。そうは言っても、この構造では、すべてのオブジェクトが無期限に存続すると想定する必要があります。

または、WeakSet で弱い参照を使用__del__して、ワーキング セットから実際に削除するようにすることもできます。ただし、(少なくとも) 循環参照が原因で呼び出されることが保証されていないため、「死んだ」オブジェクトがライブ インスタンス グローバルにまだ存在している可能性があります。

于 2012-08-09T17:12:57.810 に答える
1

これは興味深いメタクラス要件ですが、Python 3 で記述するのはかなり簡単でした (数分かかりました)。


コード:

import weakref

class InstanceIter(type):

    def __new__(cls, name, bases, class_dict):
        new_class = super().__new__(cls, name, bases, class_dict)
        new_class.__instances = weakref.WeakSet()
        return new_class

    def __call__(self, *args, **kwargs):
        instance = super().__call__(*args, **kwargs)
        self.__instances.add(instance)
        return instance

    def __iter__(self):
        return iter(self.__instances)

class SomeClass(metaclass=InstanceIter):

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '{!s}({!r})'.format(self.__class__.__name__, self.name)

コードが機能することを確認するためのテストも簡単であることが判明しました。弱参照は記憶に役立ちます。


テスト:

>>> a, b, c, d, e = (SomeClass(name) for name in 'vwxyz')
>>> tuple(SomeClass)
(SomeClass('v'), SomeClass('y'), SomeClass('x'), SomeClass('z'), SomeClass('w'))
>>> 
于 2012-08-09T19:18:25.900 に答える