私は、クラスオブジェクト(整数、文字列など)だけで構成される多くの大きなクラスを持つプロジェクトを継承しました。属性のリストを手動で定義する必要なく、属性が存在するかどうかを確認できるようにしたいと思います。
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
__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
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
これは、クラスオブジェクトを反復可能にする方法です。クラスに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ループで使用されると、IndexErrorが発生するまで、getitemが連続してより高いインデックス値で呼び出されます。
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
.__dict__
と同様に、このEnum
ベースのアプローチを使用した反復の順序は、定義の順序と同じです。
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