Je reçois new_tag
d'un champ de texte de formulaire avec self.response.get("new_tag")
et selected_tags
de champs de case à cocher avec
self.response.get_all("selected_tags")
Je les combine comme ceci:
tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
(f1.striplist
est une fonction qui supprime les espaces dans les chaînes de la liste.)
Mais dans le cas où tag_list
est vide (aucune nouvelle balise n'est entrée) mais il y a quelques selected_tags
, new_tag_list
contient une chaîne vide " "
.
Par exemple, de logging.info
:
new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']
Comment puis-je me débarrasser de la chaîne vide?
S'il y a une chaîne vide dans la liste:
>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']
Mais s'il n'y a pas de chaîne vide:
>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
i = s.index("")
del s[i]
else:
print "new_tag_list has no empty string"
Mais cela donne:
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
if new_tag_list.index(""):
ValueError: list.index(x): x not in list
Pourquoi cela se produit-il et comment puis-je y remédier?
Testez la présence à l'aide de l'opérateur in
, puis appliquez la méthode remove
.
if thing in some_list: some_list.remove(thing)
La méthode remove
ne supprime que la première occurrence de thing
. Pour supprimer toutes les occurrences, vous pouvez utiliser while
au lieu de if
.
while thing in some_list: some_list.remove(thing)
Cette attitude de tir-première-demande-dernière-question est courante en Python. Au lieu de tester à l'avance si l'objet convient, il suffit de procéder à l'opération et de détecter les exceptions pertinentes:
try:
some_list.remove(thing)
except ValueError:
pass # or scream: thing not in some_list!
except AttributeError:
call_security("some_list not quacking like a list!")
Bien entendu, la deuxième clause d’exception de l’exemple ci-dessus n’est pas seulement d’humour discutable, elle est tout à fait inutile (il s’agissait d’illustrer le typage de canards pour des personnes qui ne connaissaient pas le concept).
Si vous vous attendez à plusieurs occurrences de chose:
while True:
try:
some_list.remove(thing)
except ValueError:
break
Cependant, avec contextmanager de suppress () de contextlib (introduit dans python 3.4), le code ci-dessus peut être simplifié:
with suppress(ValueError, AttributeError):
some_list.remove(thing)
Encore une fois, si vous vous attendez à plusieurs occurrences de chose:
with suppress(ValueError):
while True:
some_list.remove(thing)
Vers 1993, Python reçut lambda
, reduce()
, filter()
et map()
, gracieuseté d'un pirate LISP les a ratées et a soumis des correctifs de travail *. Vous pouvez utiliser filter
pour supprimer des éléments de la liste:
is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)
Il existe un raccourci qui peut être utile dans votre cas: si vous souhaitez filtrer les éléments vides (en fait, les éléments où bool(item) == False
, comme None
, zéro, les chaînes vides ou d'autres collections vides), vous pouvez passer None comme premier argument:
cleaned_list = filter(None, some_list)
filter(function, iterable)
était équivalent à [item for item in iterable if function(item)]
( ou [item for item in iterable if item]
si le premier argument est None
); dans Python 3.x, il est maintenant équivalent à (item for item in iterable if function(item))
. La différence subtile est que le filtre utilisé pour renvoyer une liste, fonctionne maintenant comme une expression de générateur - cela n’est pas grave si vous ne parcourez que la liste nettoyée et que vous la supprimez, mais si vous avez vraiment besoin d’une liste, vous devez inclure le filter()
appel avec le constructeur list()
.filter
- avec ses compagnons map
et reduce
(ils ne sont pas encore partis mais reduce
a été déplacé. dans le module functools , qui vaut le détour si vous voulez fonctions de poids fort ).Compréhensions de liste est devenu le style préféré pour la manipulation de liste dans Python depuis son introduction dans la version 2.0 par PEP 202 . Cela s'explique par le fait que la compréhension de liste fournit un moyen plus concis de créer des listes dans les situations où map()
et filter()
et/ou des boucles imbriquées seraient actuellement utilisées.
cleaned_list = [ x for x in some_list if x is not thing ]
Les expressions de générateur ont été introduites dans la version 2.4 par PEP 289 . Une expression de générateur est préférable pour les situations dans lesquelles vous n'avez pas vraiment besoin (ou souhaitez) d'avoir une liste complète créée en mémoire, comme lorsque vous souhaitez simplement parcourir les éléments un à un. Si vous ne parcourez que la liste, vous pouvez considérer une expression génératrice comme une compréhension liste paresseuse :
for item in (x for x in some_list if x is not thing):
do_your_thing_with(item)
!=
au lieu de is not
( la différence est importante )try:
s.remove("")
except ValueError:
print "new_tag_list has no empty string"
Notez que cela ne supprimera qu'une instance de la chaîne vide de votre liste (comme votre code l'aurait aussi). Votre liste peut-elle en contenir plusieurs?
Si index
ne trouve pas la chaîne recherchée, il jette le ValueError
que vous voyez. Soit attraper le ValueError:
try:
i = s.index("")
del s[i]
except ValueError:
print "new_tag_list has no empty string"
o utilise find
, qui renvoie -1 dans ce cas.
i = s.find("")
if i >= 0:
del s[i]
else:
print "new_tag_list has no empty string"
Ajouter cette réponse pour compléter, bien que ce ne soit utilisable que sous certaines conditions.
Si vous avez des listes très volumineuses, supprimer de la fin de la liste évite aux éléments internes de CPython de devoir memmove
, dans les cas où vous pouvez réorganiser la liste. Cela donne un gain de performance à supprimer de la fin de la liste, car il n’aura pas besoin de memmove
every item après celui que vous supprimez - de retour en arrière (1).
Pour les retraits ponctuels, la différence de performances peut être acceptable, mais si vous avez une longue liste et devez supprimer de nombreux éléments, vous remarquerez probablement un impact négatif sur les performances.
Certes, dans ces cas, une recherche dans une liste complète risque également de gêner les performances, à moins que les éléments figurent généralement en tête de liste.
Cette méthode peut être utilisée pour une élimination plus efficace,
tant que la réorganisation de la liste est acceptable. (2)
def remove_unordered(ls, item):
i = ls.index(item)
ls[-1], ls[i] = ls[i], ls[-1]
ls.pop()
Vous voudrez peut-être éviter de générer une erreur lorsque le item
ne figure pas dans la liste.
def remove_unordered_test(ls, item):
try:
i = ls.index(item)
except ValueError:
return False
ls[-1], ls[i] = ls[i], ls[-1]
ls.pop()
return True
Un moyen simple de tester cela consiste à comparer la différence de vitesse entre supprimer du début de la liste et supprimer le dernier élément:
python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'
Avec:
python -m timeit 'a = [0] * 100000' 'while a: a.pop()'
(donne un ordre de grandeur différence de vitesse où le deuxième exemple est plus rapide avec CPython et PyPy).
set
, en particulier si la liste n'est pas destinée à stocker des doublons.set
. Vérifiez également si les données peuvent être commandées.Eek, ne fais rien d'aussi compliqué:)
Juste filter()
vos balises. bool()
renvoie False
pour les chaînes vides, donc au lieu de
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
tu devrais écrire
new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))
ou mieux encore, placez cette logique dans striplist()
afin qu'elle ne renvoie pas de chaîne vide.
Voici une autre approche à ne pas manquer:
next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)
Il ne crée pas de copie de liste, ne passe pas plusieurs fois dans la liste, ne nécessite pas de gestion des exceptions supplémentaire et renvoie l'objet correspondant ou None s'il n'y a pas de correspondance. Le seul problème est que cela fait une longue déclaration.
En général, lorsque vous recherchez une solution one-liner qui ne génère pas d'exceptions, next () est la meilleure solution, car c'est l'une des rares fonctions Python qui prend en charge un argument par défaut.
Tout ce que vous avez à faire est ceci
list = ["a", "b", "c"]
try:
list.remove("a")
except:
print("meow")
mais cette méthode a un problème. Vous devez mettre quelque chose à la place sauf si j'ai trouvé ceci:
list = ["a", "b", "c"]
if "a" in str(list):
list.remove("a")