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