30

次のようなクラス変数でインスタンスを追跡するクラスがあります。

class Foo:
    by_id = {}

    def __init__(self, id):
        self.id = id
        self.by_id[id] = self

私ができるようにしたいのは、クラスの既存のインスタンスを反復処理することです。私はこれを行うことができます:

for foo in Foo.by_id.values():
    foo.do_something()

しかし、それは次のようにきれいに見えます:

for foo in Foo:
    foo.do_something()

これは可能ですか?classmethod を定義しようとしまし__iter__たが、うまくいきませんでした。

4

4 に答える 4

32

classを反復したい場合は、反復をサポートするメタクラスを定義する必要があります。

x.py:

class it(type):
    def __iter__(self):
        # Wanna iterate over a class? Then ask that class for iterator.
        return self.classiter()

class Foo:
    __metaclass__ = it # We need that meta class...
    by_id = {} # Store the stuff here...

    def __init__(self, id): # new isntance of class
        self.id = id # do we need that?
        self.by_id[id] = self # register istance

    @classmethod
    def classiter(cls): # iterate over class by giving all instances which have been instantiated
        return iter(cls.by_id.values())

if __name__ == '__main__':
    a = Foo(123)
    print list(Foo)
    del a
    print list(Foo)

最後にわかるように、インスタンスを削除してもオブジェクト自体には何の影響もありませんby_idweakrefsを使用して対処できます

import weakref

そして、する

by_id = weakref.WeakValueDictionary()

. このように、この場合のように、値を保持する「強力な」参照がある限り、値は保持されaます。の後del a、オブジェクトを指している弱い参照しかないため、それらを gc することができます。

sに関する警告WeakValueDictionary()のため、以下を使用することをお勧めします。

[...]
    self.by_id[id] = weakref.ref(self)
[...]
@classmethod
def classiter(cls):
    # return all class instances which are still alive according to their weakref pointing to them
    return (i for i in (i() for i in cls.by_id.values()) if i is not None)

少し複雑に見えますが、オブジェクトではなくオブジェクトを取得するようにしてくださいweakref

于 2012-05-30T10:41:56.900 に答える
10

マジック メソッドは常にクラスでルックアップされるため、クラスに追加__iter__しても反復可能にはなりません。ただし、クラスはそのメタクラスのインスタンスであるため、メタクラスは__iter__メソッドを定義する正しい場所です。

class FooMeta(type):
    def __iter__(self):
        return self.by_id.iteritems()

class Foo:
    __metaclass__ = FooMeta
    ...
于 2012-05-30T10:45:32.577 に答える
1

クラス リストを作成してから、次のように init メソッドで append を呼び出すことができます。

class Planet:
  planets_list = []

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

使用法:

p1 = Planet("earth")
p2 = Planet("uranus")

for i in Planet.planets_list:
    print(i.name)
于 2015-08-23T03:37:34.837 に答える