son que los iteradores generadores generadoras funciones funcion creacion python list iterator

que - Clase de Python accesible por iterador e índice



python funciones generadoras (2)

Podría ser una pregunta n00b, pero actualmente tengo una clase que implementa un iterador para que pueda hacer algo como

for i in class():

pero quiero poder acceder a la clase por índice y también me gusta

class()[1]

¿Cómo puedo hacer eso?

¡Gracias!



La respuesta aceptada actual de @ Ignacio Vázquez-Abrams es suficiente. Sin embargo, otros interesados ​​en esta pregunta pueden considerar heredar su clase de una clase base abstracta (como las que se encuentran en las collections.abc módulos estándar.abc ). Esto hace una serie de cosas ( probablemente también haya otras ):

  • asegura que todos los métodos que necesita para tratar su objeto "como un ____" están ahí
  • es autodocumentado, en el sentido de que alguien que lea su código pueda saber instantáneamente que tiene la intención de que su objeto "actúe como un ____".
  • permite que isinstance(myobject,SomeABC) funcione correctamente.
  • a menudo proporciona métodos auto-mágicamente por lo que no tenemos que definirlos nosotros mismos

(Tenga en cuenta que, además de lo anterior, crear su propio ABC puede permitirle probar la presencia de un método específico o un conjunto de métodos en cualquier objeto, y en base a esto declarar que ese objeto es una subclase del ABC, incluso si el objeto no hereda directamente del ABC . Consulte esta respuesta para obtener más información ) .

Ahora, como ejemplo, vamos a elegir e implementar un ABC para la clase en la pregunta original. Hay dos requisitos:

  1. la clase es iterable
  2. acceder a la clase por índice

Obviamente, esta clase va a ser una especie de colección. Entonces, lo que haremos es mirar nuestro menú de collection ABC para encontrar el ABC apropiado (tenga en cuenta que también hay ABC numéricos ). El ABC apropiado depende de qué métodos abstractos deseamos usar en nuestra clase.

Vemos que lo que buscamos es un Iterable si queremos usar el método __iter__() , que es lo que necesitamos para hacer cosas como for o in myobject: Sin embargo, un Iterable no incluye el método __getitem__() , que es lo que necesitamos para hacer cosas como myobject[i] . Entonces, tendremos que usar un ABC diferente.

En el menú collections.abc de las clases base abstractas, vemos que una Sequence es el ABC más simple para ofrecer la funcionalidad que necesitamos. Y, ¿lo verían? Tenemos la funcionalidad Iterable como método de mezcla, lo que significa que no tenemos que definirlo nosotros mismos, ¡gratis! También obtenemos __contains__ , __reversed__ , index y count . Lo cual, si lo piensas, son todas las cosas que deberían incluirse en cualquier objeto indexado. Si se olvidó de incluirlos, los usuarios de su código (¡incluido, potencialmente, usted mismo!) Podrían sentirse bastante molestos (sé que lo haría).

Sin embargo, hay un segundo ABC que también ofrece esta combinación de funcionalidad (iterable y accesible por [] ): un Mapping . ¿Cuál queremos usar?

Recordamos que el requisito es poder acceder al objeto por índice (como una list o una tuple ), es decir, no por clave (como un dict ). Por lo tanto, seleccionamos Sequence lugar de Mapping .

Es importante tener en cuenta que una Sequence es de solo lectura (como lo es un Mapping ), por lo que no nos permitirá hacer cosas como myobject[i] = value , o random.shuffle(myobject) . Si queremos poder hacer cosas como esa, tenemos que continuar hacia abajo en el menú de ABC y usar una MutableMapping (o una MutableMapping ).

Ahora podemos hacer nuestra clase. Lo definimos y lo heredamos de Sequence .

from collections.abc import Sequence class MyClass(Sequence): pass

Si intentamos usarlo, el intérprete nos dirá qué métodos debemos implementar antes de que se pueda usar (tenga en cuenta que los métodos también se enumeran en la página de documentación de Python):

>>> myobject = MyClass() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can''t instantiate abstract class MyClass with abstract methods __getitem__, __len__

Esto nos dice que si continuamos e implementamos __getitem__ y __len__ , podremos usar nuestra nueva clase. Podríamos hacerlo así en Python 3:

from collections.abc import Sequence class MyClass(Sequence): def __init__(self,L): self.L = L super().__init__() def __getitem__(self, i): return self.L[i] def __len__(self): return len(self.L) # Let''s test it: myobject = MyClass([1,2,3]) try: for idx,_ in enumerate(myobject): print(myobject[idx]) except Exception: print("Gah! No good!") raise # No Errors!

¡Funciona!