¿Python tiene un método de subcadena 'contiene' de cadena?

3599

Estoy buscando un método string.containso string.indexofen Python.

Quiero hacer:

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

Respuestas:

6266

Puedes usar el inoperador :

if "blah" not in somestring: 
    continue
Michael Mrozek
fuente
232
Bajo el capó, Python utilizará __contains__(self, item), __iter__(self)y __getitem__(self, key)en ese orden a determinar si un elemento se encuentra en un determinado contiene. Implemente al menos uno de esos métodos para que esté indisponible para su tipo personalizado.
BallpointBen
27
Solo asegúrate de que somestring no sea None. De lo contrario, obtienes unTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin
55
FWIW, esta es la forma idiomática de lograr dicho objetivo.
Trenton
66
Para cadenas, ¿ inutiliza el operador Python el algoritmo Rabin-Carp?
Sam Chats
3
@SamChats, consulte stackoverflow.com/questions/18139660/… para ver los detalles de implementación (en CPython; afaik, la especificación del idioma no exige ningún algoritmo en particular aquí).
Christoph Burschka
667

Si es solo una búsqueda de subcadenas, puede usarla string.find("substring").

Usted tiene que ser un poco cuidadoso con find, indexy insin embargo, a medida que se subcadena búsquedas. En otras palabras, esto:

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

Se imprimiría de Found 'is' in the string.manera similar, if "is" in s:se evaluaría a True. Esto puede o no ser lo que quieres.

eldarerathis
fuente
78
+1 para resaltar las trampas involucradas en las búsquedas de subcadenas. la solución obvia es if ' is ' in s:cuál regresará Falsecomo se espera (probablemente).
aaronasterling
95
@aaronasterling Obvio que puede ser, pero no del todo correcto. ¿Qué pasa si tiene puntuación o está al principio o al final? ¿Qué pasa con la capitalización? Mejor sería una búsqueda de expresiones regulares sin distinción entre mayúsculas y minúsculas \bis\b(límites de palabras).
Bob
2
@JamieBull Una vez más, debe considerar si desea incluir la puntuación como delimitador de una palabra. Dividir tendría en gran medida el mismo efecto que la solución ingenua de verificar ' is ', en particular, no atrapará This is, a comma'o no 'It is.'.
Bob
77
@JamieBull: Dudo mucho que cualquier división de entrada real s.split(string.punctuation + string.whitespace)se dividiría incluso una vez; splitno es como la familia de funciones strip/ rstrip/ lstrip, solo se divide cuando ve todos los caracteres delimitadores, contiguamente, en ese orden exacto. Si desea dividir en clases de caracteres, vuelve a las expresiones regulares (en ese momento, buscar r'\bis\b'sin dividir es la forma más sencilla y rápida de hacerlo).
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 tomado. Esto ahora es ridículo ...
Jamie Bull
190

¿Python tiene una cadena que contiene el método de subcadena?

Sí, pero Python tiene un operador de comparación que debería usar en su lugar, porque el lenguaje pretende su uso, y otros programadores esperarán que lo use. Esa palabra clave es in, que se utiliza como operador de comparación:

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

El opuesto (complemento), que pide la pregunta original, es not in:

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

Esto es semánticamente igual, not 'foo' in '**foo**'pero es mucho más legible y se proporciona explícitamente en el lenguaje como una mejora de legibilidad.

Evitar el uso __contains__, findyindex

Según lo prometido, este es el containsmétodo:

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

vuelve True. También podría llamar a esta función desde la instancia de la supercadena:

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

Pero no lo hagas. Los métodos que comienzan con guiones bajos se consideran semánticamente privados. La única razón para usar esto es cuando se extiende la funcionalidad iny not in(por ejemplo, si se subclasifica 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')

y ahora:

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

Además, evite los siguientes métodos de cadena:

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

Otros lenguajes pueden no tener métodos para probar directamente las subcadenas, por lo que tendría que usar este tipo de métodos, pero con Python, es mucho más eficiente usar el inoperador de comparación.

Comparaciones de rendimiento

Podemos comparar varias formas de lograr el mismo objetivo.

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'))),
}

Y ahora vemos que usar ines mucho más rápido que los demás. Menos tiempo para hacer una operación equivalente es mejor:

>>> 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}
Aaron Hall
fuente
66
¿Por qué se debe evitar str.indexy str.find? ¿De qué otra forma sugeriría que alguien encuentre el índice de una subcadena en lugar de solo si existe o no? (¿o quiso decir evitar usarlos en lugar de s.find(ss) != -1ss in s
contenedores
3
Precisamente, aunque la intención detrás del uso de esos métodos puede abordarse mejor mediante el uso elegante del remódulo. Todavía no he encontrado un uso para str.index o str.find en cualquier código que haya escrito todavía.
Aaron Hall
Extienda su respuesta a los consejos contra el uso str.counttambién ( string.count(something) != 0). estremecimiento
cs95
¿Cómo funciona la operatorversión del módulo ?
jpmc26
@ jpmc26 es igual que el in_anterior, pero con un marco de pila alrededor, por lo que es más lento que eso: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall
175

if needle in haystack:es el uso normal, como dice @Michael: se basa en el inoperador, más legible y más rápido que una llamada a método.

Si realmente necesita un método en lugar de un operador (por ejemplo, hacer algo extraño key=para un tipo muy peculiar ...?), Sería 'haystack'.__contains__. Pero dado que su ejemplo es para usar en un if, supongo que realmente no quiere decir lo que dice ;-). No es una buena forma (ni legible, ni eficiente) usar métodos especiales directamente; están destinados a ser utilizados, en cambio, a través de los operadores y las incorporaciones que les delegan.

Alex Martelli
fuente
55

in Python cadenas y listas

Aquí hay algunos ejemplos útiles que hablan por sí mismos sobre el inmétodo:

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

Consideración. Las listas son iterables, y el inmétodo actúa en iterables, no solo en cadenas.

firelynx
fuente
1
¿Se podría cambiar la lista iterable para buscar cualquiera de la lista en una sola cadena? Ej ["bar", "foo", "foobar"] in "foof":?
CaffeinatedCoder
1
@CaffeinatedCoder, no, esto requiere una iteración anidada. Mejor hacerlo uniendo la lista con las tuberías "|" .join (["bar", "foo", "foobar"]) y compilando una expresión regular de él, luego haciendo coincidir con "foof"
firelynx
2
any ([x en "foof" para x en ["bar", "foo", "foobar"]])
Izaak Weiss
1
@IzaakWeiss Tu único forro funciona, pero no es muy fácil de leer, y tiene una iteración anidada.
Aconsejaría no
1
@ PiyushS.Wanare, ¿qué quieres decir con complejidad? El "WTF / min" es mucho más alto con regex.
firelynx
42

Si está satisfecho con "blah" in somestringpero quiere que sea una llamada de función / método, probablemente pueda hacer esto

import operator

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

Todos los operadores en Python se pueden encontrar más o menos en el módulo del operador incluido in.

Jeffrey04
fuente
40

Entonces, aparentemente no hay nada similar para la comparación vectorial. Una forma obvia de hacerlo en Python sería:

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
Ovnis
fuente
1
Esto se debe a que hay miles de millones de formas de crear un Producto a partir de variables atómicas. Puede rellenarlos en una tupla, una lista (que son formas de productos cartesianos y vienen con un orden implícito), o pueden denominarse propiedades de una clase (sin orden a priori) o valores de diccionario, o pueden ser archivos en un directorio, o lo que sea. Siempre que pueda identificar de forma única (iter o getitem) algo en un 'contenedor' o 'contexto', puede ver ese 'contenedor' como una especie de vector y definir operaciones binarias en él. en.wikipedia.org/wiki/…
Niriel
No vale nada que inno deba usarse con listas porque hace un escaneo lineal de los elementos y es lento en comparación. Utilice un conjunto en su lugar, especialmente si las pruebas de membresía se deben realizar repetidamente.
cs95
22

Puedes usar y.count().

Devolverá el valor entero de la cantidad de veces que aparece una subcadena en una cadena.

Por ejemplo:

string.count("bah") >> 0
string.count("Hello") >> 1
Brandon Bailey
fuente
8
contar una cadena es costoso cuando solo quieres verificar si está allí ...
Jean-François Fabre
3
métodos que existen en la publicación original de 2010, así que terminé editándolos, con el consenso de la comunidad (ver meta publicación meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre
17
No. Mi punto es "¿por qué responder exactamente lo mismo que otros hicieron hace 9 años"?
Jean-François Fabre
10
porque estoy moderando el sitio ... He hecho la pregunta en meta meta.stackoverflow.com/questions/385063/…
Jean-François Fabre
2
luego, si tiene la autoridad para eliminarlo, elimínelo, de lo contrario, haga lo que debe y continúe. En mi opinión, esta respuesta agrega valor, lo que se refleja en los votos positivos de los usuarios.
Brandon Bailey
20

Aquí está tu respuesta:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Para verificar si es 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
Ytpillai
fuente
8

Puede usar expresiones regulares para obtener las ocurrencias:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Muskovets
fuente