Sous le capot, Python utilisera __contains__(self, item), __iter__(self)et __getitem__(self, key)dans cet ordre pour déterminer si un élément se trouve dans un contenu donné. Implémentez au moins une de ces méthodes pour rendre indisponible votre type personnalisé.
BallpointBen
28
Assurez-vous simplement que quelque chose ne sera pas None. Sinon, vous obtenez unTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin
6
FWIW, c'est la manière idiomatique d'atteindre cet objectif.
Trenton
7
Pour les chaînes, l' inopérateur Python utilise-t-il l'algorithme Rabin-Carp?
Sam Chats
4
@SamChats voir stackoverflow.com/questions/18139660/… pour les détails d'implémentation (en CPython; afaik la spécification du langage ne requiert aucun algorithme particulier ici).
Christoph Burschka
667
Si c'est juste une recherche de sous-chaîne que vous pouvez utiliser string.find("substring").
Vous ne devez être un peu prudent avec find, indexet insi, comme ils sont sousChaîne recherches. En d'autres termes, ceci:
s ="This be a string"if s.find("is")==-1:print("No 'is' here!")else:print("Found 'is' in the string.")
Il imprimerait de la Found 'is' in the string.même manière, if "is" in s:évaluerait True. Cela peut ou non être ce que vous voulez.
+1 pour mettre en évidence les pièges impliqués dans les recherches de sous-chaîne. la solution évidente est celle if ' is ' in s:qui reviendra Falsecomme prévu (probablement).
aaronasterling
95
@aaronasterling C'est évident, mais pas tout à fait correct. Et si vous avez une ponctuation ou si c'est au début ou à la fin? Et la capitalisation? Mieux serait une recherche de regex insensible à la casse \bis\b(limites de mots).
Bob
2
@JamieBull Encore une fois, vous devez considérer si vous souhaitez inclure la ponctuation comme délimiteur d'un mot. Le fractionnement aurait en grande partie le même effet que la solution naïve de vérification ' is ', notamment, il n'attrapera pas This is, a comma'ou 'It is.'.
Bob
7
@JamieBull: Je doute fortement que toute séparation d'entrée réelle s.split(string.punctuation + string.whitespace)se divise même une fois; splitn'est pas comme la famille de fonctions strip/ rstrip/ lstrip, elle ne se divise que lorsqu'elle voit tous les caractères délimiteurs, contigus, dans cet ordre exact. Si vous souhaitez diviser les classes de caractères, vous revenez aux expressions régulières (à quel moment, rechercher r'\bis\b'sans fractionner est le moyen le plus simple et le plus rapide).
ShadowRanger
8
'is' not in (w.lower() for w in s.translate(string.maketrans(' ' * len(string.punctuation + string.whitespace), string.punctuation + string.whitespace)).split()- ok, remarque prise. C'est maintenant ridicule ...
Jamie Bull
190
Python a-t-il une chaîne contenant une méthode de sous-chaîne?
Oui, mais Python a un opérateur de comparaison que vous devez utiliser à la place, car le langage a l'intention de l'utiliser, et d'autres programmeurs s'attendront à ce que vous l'utilisiez. Ce mot-clé est inutilisé comme opérateur de comparaison:
>>>'foo'in'**foo**'True
Le contraire (complément), que la question initiale demande, est not in:
>>>'foo'notin'**foo**'# returns FalseFalse
C'est sémantiquement le même que not 'foo' in '**foo**'mais c'est beaucoup plus lisible et explicitement prévu dans le langage comme une amélioration de la lisibilité.
Évitez d' utiliser __contains__, findetindex
Comme promis, voici la containsméthode:
str.__contains__('**foo**','foo')
retourne True. Vous pouvez également appeler cette fonction à partir de l'instance de la super chaîne:
'**foo**'.__contains__('foo')
Mais non. Les méthodes commençant par des traits de soulignement sont considérées comme sémantiquement privées. La seule raison d'utiliser cela est lors de l'extension des fonctionnalités inet not in(par exemple, si le sous-classement str):
classNoisyString(str):def __contains__(self, other):print('testing if "{0}" in "{1}"'.format(other, self))return super(NoisyString, self).__contains__(other)
ns =NoisyString('a string with a substring inside')
et maintenant:
>>>'substring'in ns
testing if"substring"in"a string with a substring inside"True
Évitez également les méthodes de chaîne suivantes:
>>>'**foo**'.index('foo')2>>>'**foo**'.find('foo')2>>>'**oo**'.find('foo')-1>>>'**oo**'.index('foo')Traceback(most recent call last):File"<pyshell#40>", line 1,in<module>'**oo**'.index('foo')ValueError: substring not found
D'autres langages peuvent ne pas avoir de méthodes pour tester directement les sous-chaînes, et vous devrez donc utiliser ces types de méthodes, mais avec Python, il est beaucoup plus efficace d'utiliser l' inopérateur de comparaison.
Comparaisons de performances
Nous pouvons comparer différentes façons d'atteindre le même objectif.
Et maintenant, nous voyons que l'utilisation inest beaucoup plus rapide que les autres. Moins de temps pour faire une opération équivalente, c'est mieux:
Pourquoi faut-il éviter str.indexet str.find? Sinon, comment suggéreriez-vous que quelqu'un trouve l'index d'une sous-chaîne au lieu de simplement savoir s'il existe ou non? (ou avez - vous évité moyen de les utiliser à la place de contient - alors ne pas utiliser au s.find(ss) != -1lieu de ss in s?)
coderforlife
3
Précisément, bien que l'intention derrière l'utilisation de ces méthodes puisse être mieux abordée par une utilisation élégante du remodule. Je n'ai pas encore trouvé d'utilisation pour str.index ou str.find moi-même dans aucun code que j'ai écrit pour le moment.
Aaron Hall
Veuillez également étendre votre réponse aux conseils contre l'utilisation str.count( string.count(something) != 0). frisson
if needle in haystack:est l'utilisation normale, comme le dit @Michael - elle repose sur l' inopérateur, plus lisible et plus rapide qu'un appel de méthode.
Si vous avez vraiment besoin d'une méthode au lieu d'un opérateur (par exemple, pour faire un peu bizarre key=pour un type très particulier ...?), Ce serait 'haystack'.__contains__. Mais comme votre exemple est destiné à être utilisé dans un if, je suppose que vous ne pensez pas vraiment ce que vous dites ;-). Ce n'est pas une bonne forme (ni lisible, ni efficace) d'utiliser directement des méthodes spéciales - elles sont plutôt destinées à être utilisées à travers les opérateurs et les commandes intégrées qui leur sont déléguées.
Voici quelques exemples utiles qui parlent d'eux-mêmes concernant la inméthode:
"foo"in"foobar"True"foo"in"Foobar"False"foo"in"Foobar".lower()True"foo".capitalize()in"Foobar"True"foo"in["bar","foo","foobar"]True"foo"in["fo","o","foobar"]False["foo"in a for a in["fo","o","foobar"]][False,False,True]
Caveat. Les listes sont des itérables, et la inméthode agit sur les itérables, pas seulement sur les chaînes.
La liste itérable pourrait-elle être inversée pour rechercher l'une des listes dans une seule chaîne? Ex ["bar", "foo", "foobar"] in "foof":?
CaffeinatedCoder
1
@CaffeinatedCoder, non, cela nécessite une itération imbriquée. Il est préférable de joindre la liste avec les tuyaux "|" .join (["bar", "foo", "foobar"]) et d'en compiler une expression
régulière
2
any ([x in "foof" for x in ["bar", "foo", "foobar"]])
Izaak Weiss
1
@IzaakWeiss Votre one liner fonctionne, mais il n'est pas très lisible et il fait une itération imbriquée. Je
déconseille de
1
@ PiyushS.Wanare qu'entendez-vous par complexité? Le "WTF / min" est beaucoup plus élevé avec regex.
firelynx
42
Si vous êtes satisfait "blah" in somestringmais souhaitez que ce soit un appel de fonction / méthode, vous pouvez probablement le faire
En effet, il existe une multitude de façons de créer un produit à partir de variables atomiques. Vous pouvez les bourrer dans un tuple, une liste (qui sont des formes de produits cartésiens et qui viennent avec un ordre implicite), ou elles peuvent être nommées propriétés d'une classe (pas d'ordre a priori) ou des valeurs de dictionnaire, ou elles peuvent être des fichiers dans un répertoire, ou autre chose. Chaque fois que vous pouvez identifier de manière unique (iter ou getitem) quelque chose dans un «conteneur» ou un «contexte», vous pouvez voir ce «conteneur» comme une sorte de vecteur et définir des opérations binaires dessus. en.wikipedia.org/wiki/…
Niriel
inNe vaut rien qui ne devrait pas être utilisé avec des listes car il effectue un balayage linéaire des éléments et est lent à comparer. Utilisez un ensemble à la place, surtout si les tests d'appartenance doivent être effectués à plusieurs reprises.
cs95
22
Vous pouvez utiliser y.count().
Il renverra la valeur entière du nombre de fois qu'une sous-chaîne apparaît dans une chaîne.
compter une chaîne coûte cher quand on veut juste vérifier si elle est là ...
Jean-François Fabre
3
méthodes qui existent dans le post original de 2010 donc j'ai fini par les éditer, avec le consensus de la communauté (voir meta post meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre
17
non. Mon point est "pourquoi répondre exactement la même chose que d'autres l'ont fait il y a 9 ans"?
Si vous avez le pouvoir de le supprimer, supprimez-le, sinon faites ce que vous devez et continuez. OMI, cette réponse ajoute de la valeur, ce qui se reflète dans les votes positifs des utilisateurs.
__contains__(self, item)
,__iter__(self)
et__getitem__(self, key)
dans cet ordre pour déterminer si un élément se trouve dans un contenu donné. Implémentez au moins une de ces méthodes pour rendrein
disponible votre type personnalisé.TypeError: argument of type 'NoneType' is not iterable
in
opérateur Python utilise-t-il l'algorithme Rabin-Carp?Si c'est juste une recherche de sous-chaîne que vous pouvez utiliser
string.find("substring")
.Vous ne devez être un peu prudent avec
find
,index
etin
si, comme ils sont sousChaîne recherches. En d'autres termes, ceci:Il imprimerait de la
Found 'is' in the string.
même manière,if "is" in s:
évalueraitTrue
. Cela peut ou non être ce que vous voulez.la source
if ' is ' in s:
qui reviendraFalse
comme prévu (probablement).\bis\b
(limites de mots).' is '
, notamment, il n'attrapera pasThis is, a comma'
ou'It is.'
.s.split(string.punctuation + string.whitespace)
se divise même une fois;split
n'est pas comme la famille de fonctionsstrip
/rstrip
/lstrip
, elle ne se divise que lorsqu'elle voit tous les caractères délimiteurs, contigus, dans cet ordre exact. Si vous souhaitez diviser les classes de caractères, vous revenez aux expressions régulières (à quel moment, rechercherr'\bis\b'
sans fractionner est le moyen le plus simple et le plus rapide).'is' not in (w.lower() for w in s.translate(string.maketrans(' ' * len(string.punctuation + string.whitespace), string.punctuation + string.whitespace)).split()
- ok, remarque prise. C'est maintenant ridicule ...Oui, mais Python a un opérateur de comparaison que vous devez utiliser à la place, car le langage a l'intention de l'utiliser, et d'autres programmeurs s'attendront à ce que vous l'utilisiez. Ce mot-clé est
in
utilisé comme opérateur de comparaison:Le contraire (complément), que la question initiale demande, est
not in
:C'est sémantiquement le même que
not 'foo' in '**foo**'
mais c'est beaucoup plus lisible et explicitement prévu dans le langage comme une amélioration de la lisibilité.Évitez d' utiliser
__contains__
,find
etindex
Comme promis, voici la
contains
méthode:retourne
True
. Vous pouvez également appeler cette fonction à partir de l'instance de la super chaîne:Mais non. Les méthodes commençant par des traits de soulignement sont considérées comme sémantiquement privées. La seule raison d'utiliser cela est lors de l'extension des fonctionnalités
in
etnot in
(par exemple, si le sous-classementstr
):et maintenant:
Évitez également les méthodes de chaîne suivantes:
D'autres langages peuvent ne pas avoir de méthodes pour tester directement les sous-chaînes, et vous devrez donc utiliser ces types de méthodes, mais avec Python, il est beaucoup plus efficace d'utiliser l'
in
opérateur de comparaison.Comparaisons de performances
Nous pouvons comparer différentes façons d'atteindre le même objectif.
Et maintenant, nous voyons que l'utilisation
in
est beaucoup plus rapide que les autres. Moins de temps pour faire une opération équivalente, c'est mieux:la source
str.index
etstr.find
? Sinon, comment suggéreriez-vous que quelqu'un trouve l'index d'une sous-chaîne au lieu de simplement savoir s'il existe ou non? (ou avez - vous évité moyen de les utiliser à la place de contient - alors ne pas utiliser aus.find(ss) != -1
lieu dess in s
?)re
module. Je n'ai pas encore trouvé d'utilisation pour str.index ou str.find moi-même dans aucun code que j'ai écrit pour le moment.str.count
(string.count(something) != 0
). frissonoperator
version du module ?in_
ci-dessus - mais avec un stackframe autour, donc c'est plus lent que ça: github.com/python/cpython/blob/3.7/Lib/operator.py#L153if needle in haystack:
est l'utilisation normale, comme le dit @Michael - elle repose sur l'in
opérateur, plus lisible et plus rapide qu'un appel de méthode.Si vous avez vraiment besoin d'une méthode au lieu d'un opérateur (par exemple, pour faire un peu bizarre
key=
pour un type très particulier ...?), Ce serait'haystack'.__contains__
. Mais comme votre exemple est destiné à être utilisé dans unif
, je suppose que vous ne pensez pas vraiment ce que vous dites ;-). Ce n'est pas une bonne forme (ni lisible, ni efficace) d'utiliser directement des méthodes spéciales - elles sont plutôt destinées à être utilisées à travers les opérateurs et les commandes intégrées qui leur sont déléguées.la source
in
Chaînes et listes PythonVoici quelques exemples utiles qui parlent d'eux-mêmes concernant la
in
méthode:Caveat. Les listes sont des itérables, et la
in
méthode agit sur les itérables, pas seulement sur les chaînes.la source
["bar", "foo", "foobar"] in "foof"
:?Si vous êtes satisfait
"blah" in somestring
mais souhaitez que ce soit un appel de fonction / méthode, vous pouvez probablement le faireTous les opérateurs en Python peuvent être plus ou moins trouvés dans le module opérateur, y compris
in
.la source
Donc, apparemment, il n'y a rien de similaire pour la comparaison vectorielle. Une façon évidente de le faire en Python serait:
la source
in
Ne vaut rien qui ne devrait pas être utilisé avec des listes car il effectue un balayage linéaire des éléments et est lent à comparer. Utilisez un ensemble à la place, surtout si les tests d'appartenance doivent être effectués à plusieurs reprises.Vous pouvez utiliser
y.count()
.Il renverra la valeur entière du nombre de fois qu'une sous-chaîne apparaît dans une chaîne.
Par exemple:
la source
Voici votre réponse:
Pour vérifier si elle est fausse:
OU:
la source
Vous pouvez utiliser des expressions régulières pour obtenir les occurrences:
la source