web-dev-qa-db-fra.com

Python: Rendre la classe iterable

J'ai hérité d'un projet avec beaucoup de grandes classes ne constituant que des objets de classe (entiers, chaînes, etc.). J'aimerais pouvoir vérifier si un attribut est présent sans qu'il soit nécessaire de définir une liste d'attributs manuellement.

Est-il possible de faire un python class iterable en utilisant la syntaxe standard? Autrement dit, j'aimerais pouvoir parcourir tous les attributs d'une classe à l'aide de for attr in Foo: (ou même de if attr in Foo) sans avoir à créer d'abord une instance de la classe. Je pense pouvoir le faire en définissant __iter__, mais je n’ai pas encore tout à fait réussi ce que je cherchais.

J'ai réalisé une partie de ce que je veux en ajoutant une méthode __iter__ comme ceci:

class Foo:
    bar = "bar"
    baz = 1
    @staticmethod
    def __iter__():
        return iter([attr for attr in dir(Foo) if attr[:2] != "__"])

Cependant, cela ne donne pas tout à fait ce que je recherche:

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

Néanmoins, cela fonctionne:

>>> for x in Foo.__iter__():
...     print(x)
bar
baz
35
multipleinterfaces

Ajoutez le __iter__ à la métaclasse au lieu de la classe elle-même (en supposant que 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

Pour Python 3.x, utilisez

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
53
Sven Marnach

Vous pouvez parcourir les attributs non masqués de la classe avec for attr in (elem for elem in dir(Foo) if elem[:2] != '__').

Une façon moins horrible d'épeler c'est:

def class_iter(Class):
    return (elem for elem in dir(Class) if elem[:2] != '__')

puis

for attr in class_iter(Foo):
    pass
8
nmichaels

c'est ainsi que nous faisons un objet de classe itérable. fournissez à la classe une méthode iter et une méthode next (), puis vous pourrez parcourir les attributs de la classe ou leurs valeurs.Vous pouvez laisser la méthode next () si vous le souhaitez ou vous pouvez définir next () et StopIteration sous certaines conditions.

par exemple:

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'

cette classe itère sur la valeur d'attribut de la classe Book. Un objet de classe peut être rendu itérable en lui fournissant également une méthode getitem . par exemple:

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

désormais, lorsque l'objet de la classe BenTen est utilisé dans une boucle for-in, getitem est appelé avec une valeur d'index successivement supérieure, jusqu'à ce qu'il lève IndexError.

7
vijay shanker

Depuis Python 3.4+, rendre une classe itérable est un peu plus facile avec 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
1
A-B-B
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
0
sanit