Python:使类可迭代
我接手了一个项目,这个项目里有很多很大的类,里面全是一些基本的数据类型,比如整数、字符串等等。我想检查一个属性是否存在,但不想手动去定义一个属性的列表。
我想知道,能不能让一个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
6 个回答
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
14
这是让一个类的对象可以被遍历的方法。你只需要给这个类提供一个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__.values():
yield 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异常。
71
把 __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