Python: сделать класс повторяемым - PullRequest
35 голосов
/ 25 марта 2011

Я унаследовал проект с множеством больших классов, состоящих только из объектов классов (целых чисел, строк и т. Д.). Я хотел бы иметь возможность проверить наличие атрибута без необходимости вручную определять список атрибутов.

Можно ли сделать Python class сам итеративным, используя стандартный синтаксис? То есть я хотел бы иметь возможность перебирать все атрибуты класса, используя for attr in Foo: (или даже if attr in Foo), без необходимости сначала создавать экземпляр класса. Я думаю, что могу сделать это, определив __iter__, но до сих пор я не совсем справился с тем, что ищу.

Я добился того, чего хотел, добавив __iter__ метод, подобный следующему:

class Foo:
    bar = "bar"
    baz = 1
    @staticmethod
    def __iter__():
        return iter([attr for attr in dir(Foo) if attr[:2] != "__"])

Однако, это не совсем то, что я ищу:

>>> for x in Foo:
...     print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'classobj' object is not iterable

Несмотря на это, это работает:

>>> for x in Foo.__iter__():
...     print(x)
bar
baz

Ответы [ 5 ]

55 голосов
/ 25 марта 2011

Добавьте __iter__ к метаклассу вместо самого класса (при условии Python 2.x):

class Foo(object):
    bar = "bar"
    baz = 1
    class __metaclass__(type):
        def __iter__(self):
            for attr in dir(self):
                if not attr.startswith("__"):
                    yield attr

Для Python 3.x используйте

class MetaFoo(type):
    def __iter__(self):
        for attr in dir(self):
            if not attr.startswith("__"):
                yield attr

class Foo(metaclass=MetaFoo):
    bar = "bar"
    baz = 1
8 голосов
/ 25 марта 2011

Вы можете перебирать скрытые атрибуты класса с помощью for attr in (elem for elem in dir(Foo) if elem[:2] != '__').

Менее ужасный способ заклинания:

def class_iter(Class):
    return (elem for elem in dir(Class) if elem[:2] != '__')

тогда

for attr in class_iter(Foo):
    pass
7 голосов
/ 01 января 2013

так мы делаем объект класса итеративным.предоставьте классу iter и метод next (), затем вы можете перебирать атрибуты класса или их значения. Вы можете оставить метод next (), если хотите, или вы можете определить next ()и вызвать StopIteration при некотором условии.

например:

class Book(object):
      def __init__(self,title,author):
          self.title = title
          self.author = author

      def __iter__(self):
          for each in self.__dict__.keys():
              yield self.__getattribute__(each)

>>> book  = Book('The Mill on the Floss','George Eliot')
>>> for each in book: each
...
'George Eliot'
'The Mill on the Floss'

этот класс выполняет итерацию по значению атрибута класса Book.Объект класса можно сделать итеративным, предоставив ему метод getitem .Например:

class BenTen(object):
    def __init__(self, bentenlist):
        self.bentenlist = bentenlist

    def __getitem__(self,index):
        if index <5:
            return self.bentenlist[index]
        else:
            raise IndexError('this is high enough')

>>> bt_obj = BenTen([x for x in range(15)])
>>>for each in bt_obj:each
...
0
1
2
3
4

теперь, когда объект класса BenTen используется в цикле for-in, getitem вызывается со значительно более высоким значением индекса, пока он не вызовет IndexError.

1 голос
/ 05 мая 2018

Начиная с Python 3.4+, сделать класс итерируемым немного проще, используя enum.Enum.

from enum import Enum

class Foo(Enum):
    bar = "qux"
    baz = 123

>>> print(*Foo)
Foo.bar Foo.baz

names = [m.name for m in Foo]
>>> print(*names)
bar baz

values = [m.value for m in Foo]
print(*values)
>>> qux 123
0 голосов
/ 22 марта 2018
class MetaItetaror(type):
    def __iter__(cls):
        return iter(
            filter(
                lambda k: not k[0].startswith('__'),
                cls.__dict__.iteritems()
            )
        )


class Klass:
    __metaclass__ = MetaItetaror

    iterable_attr_names = {'x', 'y', 'z'}
    x = 5
    y = 6
    z = 7


for v in Klass:
    print v
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...