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

Resuelto Blankman asked hace 14 años • 10 respuestas

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

Quiero hacer:

if not somestring.contains("blah"):
   continue
Blankman avatar Aug 09 '10 09:08 Blankman
Aceptado

Utilice el inoperador :

if "blah" not in somestring: 
    continue

Nota: Esto distingue entre mayúsculas y minúsculas.

Michael Mrozek avatar Aug 09 '2010 02:08 Michael Mrozek

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

Tienes que tener un poco de cuidado con find, indexy in, ya que son búsquedas de subcadenas. 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 Found 'is' in the string.de manera similar, if "is" in s:se evaluaría como True. Esto puede ser o no lo que quieres.

eldarerathis avatar Aug 09 '2010 02:08 eldarerathis

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

El 99% de los casos de uso se cubrirán utilizando la palabra clave, inque devuelve Trueo False:

'substring' in any_string

Para el caso de uso de obtener el índice, utilice str.find(que devuelve -1 en caso de error y tiene argumentos posicionales opcionales):

start = 0
stop = len(any_string)
any_string.find('substring', start, stop)

o str.index(me gusta findpero genera ValueError en caso de falla):

start = 100 
end = 1000
any_string.index('substring', start, end)

Explicación

Utilice el inoperador de comparación porque

  1. el idioma pretende su uso, y
  2. otros programadores de Python esperarán que lo uses.
>>> 'foo' in '**foo**'
True

Lo contrario (complemento), que pedía 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 está previsto explícitamente en el lenguaje como una mejora de la legibilidad.

Evitar el uso de__contains__

El método "contiene" implementa el comportamiento de in. Este ejemplo,

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

devoluciones True. También puedes 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 no públicos. La única razón para usar esto es al implementar o ampliar la funcionalidad iny not in(por ejemplo, si se crea una subclase str):

class NoisyString(str):
    def __contains__(self, other):
        print(f'testing if "{other}" in "{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

No lo use findy indexpruebe si "contiene"

No utilice los siguientes métodos de cadena para probar "contiene":

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

Es posible que otros lenguajes no tengan 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.

Además, estos no son reemplazos directos de in. Es posible que tengas que manejar la excepción o -1los casos, y si regresan 0(porque encontraron la subcadena al principio), la interpretación booleana es Falseen lugar de True.

Si realmente not any_string.startswith(substring)lo dices en serio, dilo.

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 usarlo 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}

¿Cómo puede inser más rápido que __contains__si se inusa __contains__?

Esta es una buena pregunta de seguimiento.

Desmontemos funciones con los métodos de interés:

>>> from dis import dis
>>> dis(lambda: 'a' in 'b')
  1           0 LOAD_CONST               1 ('a')
              2 LOAD_CONST               2 ('b')
              4 COMPARE_OP               6 (in)
              6 RETURN_VALUE
>>> dis(lambda: 'b'.__contains__('a'))
  1           0 LOAD_CONST               1 ('b')
              2 LOAD_METHOD              0 (__contains__)
              4 LOAD_CONST               2 ('a')
              6 CALL_METHOD              1
              8 RETURN_VALUE

entonces vemos que el .__contains__método debe buscarse por separado y luego llamarse desde la máquina virtual Python; esto debería explicar adecuadamente la diferencia.

Russia Must Remove Putin avatar Nov 25 '2014 22:11 Russia Must Remove Putin

if needle in haystack:es el uso normal, como dice @Michael: depende del inoperador, es más legible y más rápido que una llamada a un 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...?), ese sería 'haystack'.__contains__. Pero dado que su ejemplo es para usar en if, supongo que realmente no quiere decir lo que dice ;-). No es una buena forma (ni legible ni eficiente) usar métodos especiales directamente; en cambio, están destinados a usarse a través de los operadores y las funciones integradas que les delega.

Alex Martelli avatar Aug 09 '2010 03:08 Alex Martelli