我继承了一个项目,其中包含很多大类,只有类对象(整数,字符串等)。我希望能够检查属性是否存在而无需手动定义属性列表。
是否可以使用标准语法使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
答案 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