Je commence juste avec Python.
Comment obtenir un index de nombre entier 1
à partir d'une liste si la liste contient un objet booléen True
avant le 1
?
>>> lst = [True, False, 1, 3]
>>> lst.index(1)
0
>>> lst.index(True)
0
>>> lst.index(0)
1
Je pense que Python considère 0
comme False
et 1
comme True
dans l'argument de la méthode index
. Comment puis-je obtenir l'index de nombre entier 1
(c'est-à-dire 2
)?
Aussi, quel est le raisonnement ou la logique derrière le traitement d'objet booléen de cette façon dans la liste? En ce qui concerne les solutions, je peux voir que ce n’est pas si simple.
La documentation dit que
Les listes sont des séquences modifiables, généralement utilisées pour stocker des collections de Éléments homogènes (où le degré de similarité précis variera en fonction de Application).
Vous ne devez pas stocker de données hétérogènes dans des listes. L'implémentation de list.index
effectue uniquement la comparaison à l'aide de Py_EQ
(opérateur ==
). Dans votre cas, cette comparaison retourne une valeur de vérité car True
et False
ont respectivement les valeurs des entiers 1 et 0 ( la classe bool est une sous-classe de int après tout).
Cependant, vous pouvez utiliser l'expression du générateur et la fonction _next
intégrée (pour obtenir la première valeur du générateur) comme ceci:
In [4]: next(i for i, x in enumerate(lst) if not isinstance(x, bool) and x == 1)
Out[4]: 2
Ici, nous vérifions si x
est une instance de bool
before en comparant x
à 1.
Gardez à l'esprit que next
peut augmenter StopIteration
, dans ce cas, il peut être souhaitable de (re) lever ValueError
(pour imiter le comportement de list.index
).
Envelopper tout cela dans une fonction:
def index_same_type(it, val):
gen = (i for i, x in enumerate(it) if type(x) is type(val) and x == val)
try:
return next(gen)
except StopIteration:
raise ValueError('{!r} is not in iterable'.format(val)) from None
Quelques exemples:
In [34]: index_same_type(lst, 1)
Out[34]: 2
In [35]: index_same_type(lst, True)
Out[35]: 0
In [37]: index_same_type(lst, 42)
ValueError: 42 is not in iterable
Les booléens sont entiers en Python, et c'est pourquoi vous pouvez les utiliser comme n'importe quel entier:
>>> 1 + True
2
>>> [1][False]
1
[Cela ne signifie pas que vous devriez :)]
Ceci est dû au fait que bool
est une sous-classe de int
, et presque toujours un booléen se comportera comme 0 ou 1 (sauf s'il est converti en chaîne - vous obtiendrez plutôt "False"
et "True"
).
Voici une autre idée de la manière dont vous pouvez réaliser ce que vous voulez (essayez toutefois de repenser votre logique en tenant compte des informations ci-dessus):
>>> class force_int(int):
... def __eq__(self, other):
... return int(self) == other and not isinstance(other, bool)
...
>>> force_int(1) == True
False
>>> lst.index(force_int(1))
2
Ce code redéfinit la méthode int
, utilisée pour comparer les éléments de la méthode index
, pour ignorer les booléens.
Voici une solution simple et naïve à un seul support utilisant map
et Zip
:
>>> Zip(map(type, lst), lst).index((int, 1))
2
Ici, nous mappons le type de chaque élément et créons une nouvelle liste en compressant les types avec les éléments et demandons l'index de (type, value)
.
Et voici une solution itérative générique utilisant la même technique:
>>> from itertools import imap, izip
>>> def index(xs, x):
... it = (i for i, (t, e) in enumerate(izip(imap(type, xs), xs)) if (t, e) == x)
... try:
... return next(it)
... except StopIteration:
... raise ValueError(x)
...
>>> index(lst, (int, 1))
2
Ici, nous faisons essentiellement le même chose mais de manière itérative afin de ne pas nous coûter cher en termes d’efficacité mémoire/espace. Nous sommes un itérateur de la même expression que ci-dessus mais en utilisant imap
et izip
et créons une fonction d'index personnalisée qui renvoie la valeur suivante de l'itérateur ou une augmentation de ValueError
s'il n'y a pas de correspondance.
Essayez ceci.
for i, j in enumerate([True, False, 1, 3]):
if not isinstance(j, bool) and j == 1:
print i
Sortie:
2