__iter__ python

Cómo implementar__iter__(self) para un objeto contenedor(Python)



iterator python 3 (9)

He escrito un objeto contenedor personalizado.

De acuerdo con esta página , necesito implementar este método en mi objeto:

__iter__(self)

Sin embargo, al seguir el enlace a Tipos de iterador en el manual de referencia de Python, no hay ejemplos de cómo implementar uno propio.

¿Alguien puede publicar un fragmento (o un enlace a un recurso) que muestre cómo hacerlo?

El contenedor que estoy escribiendo, es un mapa (es decir, almacena valores mediante claves únicas). los dicts se pueden repetir de esta manera:

for k, v in mydict.items()

En este caso, necesito poder devolver dos elementos (¿una tupla?) En el iterador. Todavía no está claro cómo implementar dicho iterador (a pesar de las varias respuestas que se han proporcionado amablemente). ¿Podría alguien arrojar algo más de luz sobre cómo implementar un iterador para un objeto contenedor similar a un mapa? (es decir, una clase personalizada que actúa como un dict)


En caso de que no desee heredar de dict como otros han sugerido, aquí hay una respuesta directa a la pregunta sobre cómo implementar __iter__ para un ejemplo crudo de un dict personalizado:

class Attribute: def __init__(self, key, value): self.key = key self.value = value class Node(collections.Mapping): def __init__(self): self.type = "" self.attrs = [] # List of Attributes def __iter__(self): for attr in self.attrs: yield attr.key

Eso usa un generador, que está bien descrito here .

Como estamos heredando de Mapping , también debe implementar __getitem__ y __len__ :

def __getitem__(self, key): for attr in self.attrs: if key == attr.key: return attr.value raise KeyError def __len__(self): return len(self.attrs)


La "interfaz iterable" en python consta de dos métodos next () e iter (). La siguiente función es la más importante, ya que define el comportamiento del iterador, es decir, la función determina qué valor se devolverá a continuación. El método iter () se usa para restablecer el punto de inicio de la iteración. A menudo, usted encontrará que iter () puede simplemente regresar a sí mismo cuando init () se usa para establecer el punto de inicio.

Vea el siguiente código para definir un Reverso de Clase que implementa la "interfaz iterable" y define un iterador sobre cualquier instancia de cualquier clase de secuencia. El método next () comienza al final de la secuencia y devuelve los valores en orden inverso a la secuencia. Tenga en cuenta que las instancias de una clase que implementa la "interfaz de secuencia" deben definir un método len () y un método getitem ().

class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, seq): self.data = seq self.index = len(seq) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] >>> rev = Reverse(''spam'') >>> next(rev) # note no need to call iter() ''m'' >>> nums = Reverse(range(1,10)) >>> next(nums) 9


Normalmente usaría una función de generador. Cada vez que utilice una declaración de rendimiento, agregará un elemento a la secuencia.

Lo siguiente creará un iterador que devuelve cinco, y luego cada elemento en una_lista.

def __iter__(self): yield 5 for x in some_list: yield x


Otra opción es heredar de la clase base abstracta adecuada del `módulo de colecciones tal como se documenta here .

En caso de que el contenedor sea su propio iterador, puede heredar de collections.Iterator . collections.Iterator . Solo necesitas implementar el next método.

Un ejemplo es:

>>> from collections import Iterator >>> class MyContainer(Iterator): ... def __init__(self, *data): ... self.data = list(data) ... def next(self): ... if not self.data: ... raise StopIteration ... return self.data.pop() ... ... ... >>> c = MyContainer(1, "two", 3, 4.0) >>> for i in c: ... print i ... ... 4.0 3 two 1

Mientras mira el módulo de collections , considere heredar de Sequence , Mapping u otra clase base abstracta si es más apropiado. Aquí hay un ejemplo para una subclase de Sequence :

>>> from collections import Sequence >>> class MyContainer(Sequence): ... def __init__(self, *data): ... self.data = list(data) ... def __getitem__(self, index): ... return self.data[index] ... def __len__(self): ... return len(self.data) ... ... ... >>> c = MyContainer(1, "two", 3, 4.0) >>> for i in c: ... print i ... ... 1 two 3 4.0

NB : Gracias a Glenn Maynard por llamar mi atención sobre la necesidad de aclarar la diferencia entre los iteradores, por un lado, y los contenedores que son iterables en lugar de iteradores, por el otro.


Para responder la pregunta sobre las asignaciones : su __iter__ proporcionado debe iterar sobre las claves de la asignación. El siguiente es un ejemplo simple que crea un mapeo x -> x * x y funciona en Python3 extendiendo el mapeo ABC.

import collections.abc class MyMap(collections.abc.Mapping): def __init__(self, n): self.n = n def __getitem__(self, key): # given a key, return it''s value if 0 <= key < self.n: return key * key else: raise KeyError(''Invalid key'') def __iter__(self): # iterate over all keys for x in range(self.n): yield x def __len__(self): return self.n m = MyMap(5) for k, v in m.items(): print(k, ''->'', v) # 0 -> 0 # 1 -> 1 # 2 -> 4 # 3 -> 9 # 4 -> 16


Si su objeto contiene un conjunto de datos al que desea vincular el iter de su objeto, puede hacer trampa y hacer esto:

>>> class foo: def __init__(self, *params): self.data = params def __iter__(self): if hasattr(self.data[0], "__iter__"): return self.data[0].__iter__() return self.data.__iter__() >>> d=foo(6,7,3,8, "ads", 6) >>> for i in d: print i 6 7 3 8 ads 6


Una opción que podría funcionar en algunos casos es hacer que su clase personalizada herede de dict . Esto parece una elección lógica si actúa como un dict; tal vez debería ser un dict. De esta manera, obtienes iteración tipo dict gratis.

class MyDict(dict): def __init__(self, custom_attribute): self.bar = custom_attribute mydict = MyDict(''Some name'') mydict[''a''] = 1 mydict[''b''] = 2 print mydict.bar for k, v in mydict.items(): print k, ''=>'', v

Salida:

Some name a => 1 b => 2


ejemplo para inhert de dict, modifique it iter , por ejemplo, omita la tecla 2 cuando está en for loop

# method 1 class Dict(dict): def __iter__(self): keys = self.keys() for i in keys: if i == 2: continue yield i # method 2 class Dict(dict): def __iter__(self): for i in super(Dict, self).__iter__(): if i == 2: continue yield i


usualmente __iter__() solo devuelve self si ya has definido el método next () (objeto generador):

aquí hay un ejemplo ficticio de un generador:

class Test(object): def __init__(self, data): self.data = data def next(self): if not self.data: raise StopIteration return self.data.pop() def __iter__(self): return self

pero __iter__() también se puede usar así: http://mail.python.org/pipermail/tutor/2006-January/044455.html