Python ha una stringa "contiene" metodo di sottostringa?


3599

Sto cercando un metodo string.containso string.indexofin Python.

Voglio fare:

if not somestring.contains("blah"):
   continue

Risposte:


6266

Puoi usare l' inoperatore :

if "blah" not in somestring: 
    continue

232
Sotto il cofano, Python utilizzerà __contains__(self, item), __iter__(self)e __getitem__(self, key)in quell'ordine per determinare se un oggetto si trova in un dato contenitore. Implementa almeno uno di questi metodi per renderlo indisponibile per il tuo tipo personalizzato.
BallpointBen,

27
Assicurati solo che Somestring non sia Nessuno. Altrimenti ottieni unTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin il

5
FWIW, questo è il modo idiomatico per raggiungere detto obiettivo.
Trenton,

6
Per le stringhe, l' inoperatore Python utilizza l'algoritmo Rabin-Carp?
Sam Chats,

3
@SamChats vedono stackoverflow.com/questions/18139660/... per i dettagli di implementazione (in CPython; per quanto ne so le specifiche del linguaggio non impone alcuna particolare algoritmo qui).
Christoph Burschka,

667

Se è solo una ricerca di sottostringa che puoi usare string.find("substring").

Non c'è bisogno di essere un po 'attenti con find, indexe inanche se, come essi sono SUBSTRING ricerche. In altre parole, questo:

s = "This be a string"
if s.find("is") == -1:
    print("No 'is' here!")
else:
    print("Found 'is' in the string.")

Stamperebbe allo Found 'is' in the string.stesso modo, if "is" in s:valuterebbe a True. Questo può o meno essere quello che vuoi.


78
+1 per evidenziare i gotcha coinvolti nelle ricerche di sottostringa. l'ovvia soluzione è if ' is ' in s:che tornerà Falsecome è (probabilmente) previsto.
aaronasterling

95
@aaronasterling Ovvio che potrebbe essere, ma non del tutto corretto. E se hai la punteggiatura o è all'inizio o alla fine? E la capitalizzazione? Meglio sarebbe una ricerca regex insensibile al maiuscolo / minuscolo \bis\b(limiti di parole).
Bob,

2
@JamieBull Ancora una volta, devi considerare se vuoi includere la punteggiatura come delimitatore per una parola. La scissione avrebbe in gran parte lo stesso effetto della soluzione ingenua di controllo ' is ', in particolare, non catturerà This is, a comma'o 'It is.'.
Bob,

7
@JamieBull: dubito fortemente che qualsiasi split di input reale con s.split(string.punctuation + string.whitespace)si dividerebbe anche una volta; splitnon è come la strip/ rstrip/ lstripfamiglia di funzioni, si divide solo quando vede tutti i caratteri delimitatori, contigui, nell'ordine esatto. Se vuoi dividere in classi di personaggi, sei tornato alle espressioni regolari (a quel punto, cercare r'\bis\b'senza dividere è il modo più semplice e veloce per andare).
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, punto preso. Questo è ora ridicolo ...
Jamie Bull,

190

Python ha una stringa che contiene il metodo di sottostringa?

Sì, ma Python ha un operatore di confronto che dovresti usare invece, perché la lingua ne prevede l'utilizzo e altri programmatori si aspettano che tu lo usi. Quella parola chiave è in, che viene utilizzata come operatore di confronto:

>>> 'foo' in '**foo**'
True

Il contrario (complemento), che la domanda originale chiede, è not in:

>>> 'foo' not in '**foo**' # returns False
False

Questo è semanticamente lo stesso not 'foo' in '**foo**'ma è molto più leggibile ed esplicitamente previsto nella lingua come miglioramento della leggibilità.

Evitare l'uso __contains__, findeindex

Come promesso, ecco il containsmetodo:

str.__contains__('**foo**', 'foo')

ritorna True. Puoi anche chiamare questa funzione dall'istanza della superstring:

'**foo**'.__contains__('foo')

Ma non farlo. I metodi che iniziano con i trattini bassi sono considerati semanticamente privati. L'unico motivo per utilizzare questo è quando si estende la funzionalità ine not in(ad esempio se la sottoclasse str):

class NoisyString(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')

e adesso:

>>> 'substring' in ns
testing if "substring" in "a string with a substring inside"
True

Inoltre, evitare i seguenti metodi di stringa:

>>> '**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

Altre lingue potrebbero non avere metodi per testare direttamente le sottostringhe, quindi dovresti usare questi tipi di metodi, ma con Python è molto più efficiente usare l' inoperatore di confronto.

Confronti di prestazioni

Possiamo confrontare vari modi per raggiungere lo stesso obiettivo.

import timeit

def in_(s, other):
    return other in s

def contains(s, other):
    return s.__contains__(other)

def find(s, other):
    return s.find(other) != -1

def index(s, other):
    try:
        s.index(other)
    except ValueError:
        return False
    else:
        return True



perf_dict = {
'in:True': min(timeit.repeat(lambda: in_('superstring', 'str'))),
'in:False': min(timeit.repeat(lambda: in_('superstring', 'not'))),
'__contains__:True': min(timeit.repeat(lambda: contains('superstring', 'str'))),
'__contains__:False': min(timeit.repeat(lambda: contains('superstring', 'not'))),
'find:True': min(timeit.repeat(lambda: find('superstring', 'str'))),
'find:False': min(timeit.repeat(lambda: find('superstring', 'not'))),
'index:True': min(timeit.repeat(lambda: index('superstring', 'str'))),
'index:False': min(timeit.repeat(lambda: index('superstring', 'not'))),
}

E ora vediamo che l'utilizzo inè molto più veloce degli altri. Meno tempo per eseguire un'operazione equivalente è meglio:

>>> perf_dict
{'in:True': 0.16450627865128808,
 'in:False': 0.1609668098178645,
 '__contains__:True': 0.24355481654697542,
 '__contains__:False': 0.24382793854783813,
 'find:True': 0.3067379407923454,
 'find:False': 0.29860888058124146,
 'index:True': 0.29647137792585454,
 'index:False': 0.5502287584545229}

6
Perché si dovrebbe evitare str.indexe str.find? In quale altro modo suggeriresti che qualcuno trovi l'indice di una sottostringa anziché solo se esiste o no? (o intendevi evitare di usarli al posto di contiene - quindi non usare s.find(ss) != -1invece di ss in s?)
coderforlife

3
Proprio così, sebbene l'intento dietro l'uso di questi metodi possa essere affrontato meglio con un uso elegante del remodulo. Non ho ancora trovato un uso per str.index o str.find me stesso in qualsiasi codice che ho ancora scritto.
Aaron Hall

Si prega di estendere la risposta ai consigli contro l'utilizzo str.countanche ( string.count(something) != 0). brivido
cs95

Come funziona la operatorversione del modulo ?
jpmc26,

@ jpmc26 è lo stesso di in_sopra - ma con uno stackframe attorno, quindi è più lento di così: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall

175

if needle in haystack:è l'uso normale, come dice @Michael - si basa sull'operatore in, più leggibile e più veloce di una chiamata di metodo.

Se avessi davvero bisogno di un metodo anziché di un operatore (ad esempio fare qualcosa di strano key=per un tipo molto particolare ...?), Sarebbe 'haystack'.__contains__. Ma dal momento che il tuo esempio è da usare in un if, suppongo che non intendi davvero quello che dici ;-). Non è una buona forma (né leggibile, né efficiente) utilizzare direttamente metodi speciali - sono pensati per essere usati, invece, attraverso gli operatori e i builtin che li delegano.


55

in Stringhe ed elenchi di Python

Ecco alcuni esempi utili che parlano da soli riguardo al inmetodo:

"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]

Avvertimento. Gli elenchi sono iterabili e il inmetodo agisce sugli iterabili, non solo sulle stringhe.


1
L'elenco iterabile può essere cambiato per cercare qualsiasi elenco in una singola stringa? Es ["bar", "foo", "foobar"] in "foof":?
CaffeinatedCoder

1
@CaffeinatedCoder, no, questo richiede iterazione nidificata. È meglio unirsi alla lista con le pipe "|" .join (["bar", "foo", "foobar"]) e compilare una regex da essa, quindi abbinando su "foof"
firelynx

2
any ([x in "foof" per x in ["bar", "foo", "foobar"]])
Izaak Weiss,

1
@IzaakWeiss Il tuo unico liner funziona, ma non è molto leggibile e esegue l'iterazione annidata. Vorrei
sconsigliare di

1
@ PiyushS.Wanare cosa intendi per complessità? Il "WTF / min" è molto più alto con regex.
firelynx,

42

Se sei soddisfatto "blah" in somestringma vuoi che sia una chiamata funzione / metodo, probabilmente puoi farlo

import operator

if not operator.contains(somestring, "blah"):
    continue

Tutti gli operatori in Python possono essere trovati più o meno nel modulo operatore incluso in.


40

Quindi apparentemente non c'è nulla di simile per il confronto vettoriale. Un ovvio modo Python per farlo sarebbe:

names = ['bob', 'john', 'mike']
any(st in 'bob and john' for st in names) 
>> True

any(st in 'mary and jane' for st in names) 
>> False

1
Questo perché ci sono molti modi per creare un Prodotto da variabili atomiche. Puoi inserirli in una tupla, in un elenco (che sono forme di prodotti cartesiani e vengono forniti con un ordine implicito), oppure possono essere denominati proprietà di una classe (nessun ordine a priori) o valori di dizionario, oppure possono essere file in una directory o qualsiasi altra cosa. Ogni volta che puoi identificare (iter o getitem) in modo univoco qualcosa in un 'contenitore' o 'contesto', puoi vedere quel 'contenitore' come una sorta di vettore e definire operazioni binarie su di esso. en.wikipedia.org/wiki/…
Niriel

Non vale nulla che innon dovrebbe essere usato con le liste perché fa una scansione lineare degli elementi ed è lento rispetto. Utilizzare invece un set, soprattutto se i test di appartenenza devono essere eseguiti ripetutamente.
cs95,

22

È possibile utilizzare y.count().

Restituirà il valore intero del numero di volte in cui una stringa secondaria appare in una stringa.

Per esempio:

string.count("bah") >> 0
string.count("Hello") >> 1

8
contare una stringa è costoso quando vuoi solo controllare se è lì ...
Jean-François Fabre

3
metodi esistenti nel post originale del 2010, quindi ho finito per modificarli, con il consenso della community (vedi meta post meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre

17
no. Il mio punto è "perché rispondere esattamente alla stessa cosa di altri 9 anni fa"?
Jean-François Fabre

10
perché sto moderando il sito ... Ho posto la domanda su meta meta.stackoverflow.com/questions/385063/…
Jean-François Fabre

2
allora Se hai l'autorità per rimuoverlo, rimuovilo, altrimenti fai ciò che devi e vai avanti. IMO questa risposta aggiunge valore, che si riflette nei voti positivi degli utenti.
Brandon Bailey,

20

Ecco la tua risposta:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Per verificare se è falso:

if not "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

O:

if "insert_char_or_string_here" not in "insert_string_to_search_here":
    #DOSTUFF

8

È possibile utilizzare espressioni regolari per ottenere le occorrenze:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.