Python:使类可迭代

mul*_*ces 37 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] != "__"])
Run Code Online (Sandbox Code Playgroud)

但是,这并不能完全满足我的需求:

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

即便如此,这仍然有效:

>>> for x in Foo.__iter__():
...     print(x)
bar
baz
Run Code Online (Sandbox Code Playgroud)

Sve*_*ach 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
Run Code Online (Sandbox Code Playgroud)

对于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
Run Code Online (Sandbox Code Playgroud)

  • @aix:OP的方法不起作用的原因是`__iter__`方法仅适用于类的*实例*.这会将`__iter__`方法颠覆到元类的实例,即类. (7认同)
  • @aix:作为其他魔术方法,`__iter__`在对象类型的名称空间中查找,而不是在对象的名称空间本身中查找.我没有在Python文档中找到这个解释,但可以在[源代码]中轻松看到(http://svn.python.org/view/python/tags/r271/Objects/abstract.c?view = markup)(搜索`PyObject_GetIter()`的定义. (2认同)
  • @trudolf:[查询实例类型的特殊方法,而不是实例本身](https://docs.python.org/2.7/reference/datamodel.html#new-style-special-lookup).如果实例是一个类,这意味着在元类上查找特殊方法,而不是类本身. (2认同)

nmi*_*els 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] != '__')
Run Code Online (Sandbox Code Playgroud)

然后

for attr in class_iter(Foo):
    pass
Run Code Online (Sandbox Code Playgroud)


vij*_*ker 7

这就是我们如何使类对象可迭代.为类提供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'
Run Code Online (Sandbox Code Playgroud)

此类迭代类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
Run Code Online (Sandbox Code Playgroud)

现在当BenTen类的对象用于for-in循环时,getitem被调用具有更高的索引值,直到它引发IndexError.

  • 尽管这不是 OP 问题的答案,但它对我有所帮助,因为我在搜索可迭代类时一直在寻找它。 (3认同)
  • 这会迭代类的实例的属性(即“book = Book(...)”中的“book”);问题是直接迭代 _class_ 属性(即 `class Book(object):` 中的 `Book`)。 (2认同)