Подтвердить что ты не робот

Могу ли я перебирать класс в Python?

У меня есть класс, который отслеживает его экземпляры в переменной класса, примерно так:

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__, но это не сработало.

4b9b3361

Ответ 1

Если вы хотите перебирать класс, вам нужно определить метакласс, поддерживающий итерацию.

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_id dict. Вы можете справиться с этим, используя weakref, когда вы

import weakref

а затем do

by_id = weakref.WeakValueDictionary()

. Таким образом, значения будут сохраняться до тех пор, пока в этом случае существует "сильная" ссылка, например a. После del a есть только слабые ссылки, указывающие на объект, поэтому они могут быть gc'ed.

Из-за предупреждения относительно WeakValueDictionary() s, я предлагаю использовать следующее:

[...]
    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.

Ответ 2

Магические методы всегда ищут класс, поэтому добавление __iter__ в класс не сделает его итерабельным. Однако класс является экземпляром своего метакласса, поэтому метакласс является правильным местом для определения метода __iter__.

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

class Foo:
    __metaclass__ = FooMeta
    ...

Ответ 3

Попробуйте следующее:

Вы можете создать список с глобальной областью, определить список в основном модуле следующим образом:

fooList = []

Затем добавьте:

class Foo:
  def __init__(self):
    fooList.append(self)

для init класса foo

Затем каждый раз, когда вы создаете экземпляр класса Foo, он добавляется в список fooList.

Теперь все, что вам нужно сделать, это перебрать массив таких объектов, как этот

for f in fooList:
    f.doSomething()

Ответ 4

Вы можете создать список классов, а затем вызвать append в методе init следующим образом:

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)