Comment les fonctions any
et all
de Python fonctionnent-elles ?
any
et all
prennent des itérables et renvoient True
si tous (respectivement) les éléments sont True
.
>>> any([0, 0.0, False, (), '0']), all([1, 0.0001, True, (False,)])
(True, True) # ^^^-- chaîne non vide équivalente à True
>>> any([0, 0.0, False, (), '']), all([1, 0.0001, True, (False,), {}])
(False, False) # ^^-- équivalent à False
Si les itérables sont vides, any
renvoie False
, et all
renvoie True
.
>>> any([]), all([])
(False, True)
Aujourd'hui, j'enseignais aux étudiants en classe comment utiliser all
et any
. Ils étaient surtout confus au sujet des valeurs de retour pour les itérables vides. En expliquant de cette façon, beaucoup de compréhensions ont émergé.
Comportement de raccourci
Les fonctions any
et all
cherchent un condition qui leur permet d'arrêter l'évaluation. Les premiers exemples nécessitaient d'évaluer le booléen pour chaque élément dans la liste entière.
(Notez que la liste littérale n'est pas elle-même évaluée de manière paresseuse - vous pourriez obtenir cela avec un Itérateur - mais c'est juste à des fins illustratives.)
Voici une implémentation Python de any et all :
def any(iterable):
for i in iterable:
if i:
return True
return False # pour un itérable vide, any renvoie False!
def all(iterable):
for i in iterable:
if not i:
return False
return True # pour un itérable vide, all renvoie True!
Évidemment, les implémentations réelles sont écrites en C et sont beaucoup plus performantes, mais vous pourriez substituer le code ci-dessus et obtenir les mêmes résultats pour le code dans cette (ou toute autre) réponse.
all
all
vérifie que les éléments soient False
(pour pouvoir renvoyer False
), puis renvoie True
si aucun d'entre eux n'était False
.
>>> all([1, 2, 3, 4]) # doit tester jusqu'à la fin!
True
>>> all([0, 1, 2, 3, 4]) # 0 est False dans un contexte booléen!
False # ^-- s'arrête ici !
>>> all([])
True # arrive à la fin, donc True!
any
La façon dont any
fonctionne est qu'elle vérifie que les éléments soient True
(pour pouvoir renvoyer True
), puis renvoie False
si aucun d'entre eux n'était True
.
>>> any([0, 0.0, '', (), [], {}]) # doit tester jusqu'à la fin!
False
>>> any([1, 0, 0.0, '', (), [], {}]) # 1 est True dans un contexte booléen!
True # ^-- s'arrête ici !
>>> any([])
False # arrive à la fin, donc False!
Je pense que si vous gardez à l'esprit le comportement de raccourci, vous comprendrez intuitivement comment elles fonctionnent sans avoir besoin de consulter une table de vérité.
Preuves du comportement de raccourci de all
et any
:
Tout d'abord, créons un itérateur bruyant :
def noisy_iterator(iterable):
for i in iterable:
print('yielnd ' + repr(i))
yield i
et maintenant itérons simplement sur les listes de manière bruyante, en utilisant nos exemples :
>>> all(noisy_iterator([1, 2, 3, 4]))
yielnd 1
yielnd 2
yielnd 3
yielnd 4
True
>>> all(noisy_iterator([0, 1, 2, 3, 4]))
yielnd 0
False
Nous pouvons voir que all
s'arrête au premier check de booléen False.
Et any
s'arrête au premier check de booléen True :
>>> any(noisy_iterator([0, 0.0, '', (), [], {}]))
yielnd 0
yielnd 0.0
yielnd ''
yielnd ()
yielnd []
yielnd {}
False
>>> any(noisy_iterator([1, 0, 0.0, '', (), [], {}]))
yielnd 1
True
La source
Examinons la source pour confirmer ce qui précède.
Voici la source pour any
:
static PyObject *
builtin_any(PyObject *module, PyObject *iterable)
{
PyObject *it, *item;
PyObject *(*iternext)(PyObject *);
int cmp;
it = PyObject_GetIter(iterable);
if (it == NULL)
return NULL;
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
item = iternext(it);
if (item == NULL)
break;
cmp = PyObject_IsTrue(item);
Py_DECREF(item);
if (cmp < 0) {
Py_DECREF(it);
return NULL;
}
if (cmp > 0) {
Py_DECREF(it);
Py_RETURN_TRUE;
}
}
Py_DECREF(it);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
return NULL;
}
Py_RETURN_FALSE;
}
Et voici la source pour all
:
static PyObject *
builtin_all(PyObject *module, PyObject *iterable)
{
PyObject *it, *item;
PyObject *(*iternext)(PyObject *);
int cmp;
it = PyObject_GetIter(iterable);
if (it == NULL)
return NULL;
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
item = iternext(it);
if (item == NULL)
break;
cmp = PyObject_IsTrue(item);
Py_DECREF(item);
if (cmp < 0) {
Py_DECREF(it);
return NULL;
}
if (cmp == 0) {
Py_DECREF(it);
Py_RETURN_FALSE;
}
}
Py_DECREF(it);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
return NULL;
}
Py_RETURN_TRUE;
}