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

23

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

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()

Это возможно? Я попытался определить метод класса__iter__, но это не сработало.

К счастью, время жизни экземпляров не является проблемой. Но мне бы очень хотелось посмотреть, как это должно быть сделано. xorsyst
Имейте в виду, однако, что вы никогда не избавитесь от своих случаев. Вы должны работать со слабыми ссылками. glglgl
Пожалуйста, покажите ваш метод класса__iter__и пример того, как это не удалось. Marcin

Ваш Ответ

4   ответа
1

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

fooList = []

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

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

кinit из класса Foo

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

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

for f in fooList:
    f.doSomething()
27

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

import weakref

а затем сделать

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 объект.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededdocs.python.org/library/…
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded__classiter__.
9

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

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

class Foo:
    __metaclass__ = FooMeta
    ...
2

следующим образом:

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)

Похожие вопросы