Python:使类可迭代

时间:2011-03-25 15:15:31

标签: python syntax attributes static-methods loops

我继承了一个项目,其中包含很多大类,只有类对象(整数,字符串等)。我希望能够检查属性是否存在而无需手动定义属性列表。

是否可以使用标准语法使python 本身可迭代?也就是说,我希望能够使用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 个答案:

答案 0 :(得分:55)

__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

答案 1 :(得分:8)

您可以使用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

答案 2 :(得分:7)

这就是我们如何使类对象可迭代。为类提供 iter 和next()方法,然后你可以迭代类属性或它们的值。如果你愿意,可以保留next()方法,或者你可以定义next(并且在某些条件下提高StopIteration。

e.g:

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 方法,可以使类对象可迭代。 e.g:

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。

答案 3 :(得分:1)

从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

答案 4 :(得分:0)

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