Peut-être une question n00b, mais j'ai actuellement une classe qui implémente un itérateur afin que je puisse faire quelque chose comme
for i in class():
mais je veux pouvoir accéder à la classe par index aussi bien
class()[1]
Comment puis je faire ça?
Merci!
Implémentez les méthodes __iter__()
et __getitem__()
et autres.
Le courant réponse acceptée de @Ignacio Vazquez-Abrams est suffisant. Cependant, d'autres personnes intéressées par cette question peuvent envisager d'hériter leur classe d'une classe de base abstraite (ABC
) (comme celles trouvées dans le module standard collections.abc
). Cela fait un certain nombre de choses ( il y en a probablement aussi d'autres ):
isinstance(myobject,SomeABC)
de fonctionner correctement.(Notez que, en plus de ce qui précède, créer votre propre ABC
peut vous permettre de tester la présence d'une méthode spécifique ou d'un ensemble de méthodes dans n'importe quel objet, et basé sur sur ceci pour déclarer que cet objet est une sous-classe du ABC
, même si l'objet n'hérite pas du ABC
directement. Voir cette réponse pour plus d'informations. )
list
en utilisant ABC
Maintenant, à titre d'exemple, choisissons et implémentons un ABC
pour la classe dans la question d'origine. Il y a deux exigences:
De toute évidence, cette classe va être une sorte de collection. Donc, ce que nous allons faire est de regarder notre menu de collection
ABC's pour trouver le ABC
approprié (notez qu'il y a aussi numeric
ABC ). Le ABC
approprié dépend des méthodes abstraites que nous souhaitons utiliser dans notre classe.
Nous voyons qu'un Iterable
est ce que nous recherchons si nous voulons utiliser la méthode __iter__()
, ce dont nous avons besoin pour faire des choses comme for o in myobject:
. Cependant, un Iterable
n'inclut pas la méthode __getitem__()
, ce dont nous avons besoin pour faire des choses comme myobject[i]
. Nous aurons donc besoin d'utiliser un ABC
différent.
Dans le menu collections.abc
Des classes de base abstraites, nous voyons qu'un Sequence
est le ABC
le plus simple pour offrir les fonctionnalités dont nous avons besoin. Et - pourriez-vous regarder cela - nous obtenons la fonctionnalité Iterable
comme méthode de mixage - ce qui signifie que nous n'avons pas à la définir nous-mêmes - gratuitement! Nous obtenons également __contains__
, __reversed__
, index
et count
. Ce qui, si vous y réfléchissez, est tout ce qui devrait être inclus dans tout objet indexé. Si vous aviez oublié de les inclure, les utilisateurs de votre code (y compris, potentiellement, vous-même!) Pourraient être assez ennuyés (je sais que je le ferais).
Cependant, il existe un deuxième ABC
qui offre également cette combinaison de fonctionnalités (itérable et accessible par []
): A Mapping
. Lequel voulons-nous utiliser?
Nous rappelons que l'exigence est de pouvoir accéder à l'objet par index (comme un list
ou un Tuple
), c'est-à-dire pas par clé (comme un dict
). Par conséquent, nous sélectionnons Sequence
au lieu de Mapping
.
Barre latérale: Il est important de noter qu'un Sequence
est en lecture seule (tout comme un Mapping
), il ne nous permettra donc pas de faire des choses comme myobject[i] = value
Ou random.shuffle(myobject)
. Si nous voulons pouvoir faire des choses comme ça, nous devons continuer dans le menu de ABC
s et utiliser un MutableSequence
(ou un MutableMapping
), ce qui nécessitera l'implémentation de plusieurs méthodes supplémentaires.
Maintenant, nous pouvons faire notre classe. Nous le définissons et le faisons hériter de Sequence
.
from collections.abc import Sequence
class MyClass(Sequence):
pass
Si nous essayons de l'utiliser, l'interpréteur nous dira quelles méthodes nous devons implémenter avant qu'il puisse être utilisé (notez que les méthodes sont également répertoriées sur la page de documentation Python docs):
>>> 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__
Cela nous dit que si nous allons de l'avant et implémentons __getitem__
Et __len__
, Nous pourrons utiliser notre nouvelle classe. Nous pourrions le faire comme ceci dans 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!