O Python possui um método de substring de string 'contém'?

3599

Estou procurando um método string.containsou string.indexofem Python.

Eu quero fazer:

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

Respostas:

6265

Você pode usar o inoperador :

if "blah" not in somestring: 
    continue
Michael Mrozek
fonte
232
Sob o capô, o Python usará __contains__(self, item), __iter__(self)e __getitem__(self, key)nessa ordem para determinar se um item está em um determinado conteúdo. Implemente pelo menos um desses métodos para indisponibilizar seu tipo personalizado.
usar o seguinte
27
Apenas certifique-se de que somestring não será None. Caso contrário, você terá umTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin
5
FWIW, esta é a maneira idiomática de alcançar esse objetivo.
Trenton
6
Para strings, o inoperador Python usa o algoritmo Rabin-Carp?
Sam Chats
3
@SamChats consulte stackoverflow.com/questions/18139660/… para obter os detalhes da implementação (no CPython; depois que a especificação da linguagem não exige nenhum algoritmo específico aqui).
Christoph Burschka
667

Se for apenas uma pesquisa de substring, você pode usar string.find("substring").

Você tem que ser um pouco cuidadoso com find, indexe, inno entanto, como eles são substring pesquisas. Em outras palavras, isso:

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

Seria imprimir Found 'is' in the string.Da mesma forma, if "is" in s:avaliaria para True. Isso pode ou não ser o que você deseja.

eldarerathis
fonte
78
+1 para destacar as dicas envolvidas nas pesquisas de substring. a solução óbvia é a if ' is ' in s:que retornará Falsecomo é (provavelmente) esperado.
precisa saber é o seguinte
95
@aaronasterling Óbvio que pode ser, mas não totalmente correto. E se você tiver pontuação ou no início ou no final? E a capitalização? Melhor seria uma pesquisa de regex que não diferencia maiúsculas de minúsculas \bis\b(limites de palavras).
Bob
2
@JamieBull Mais uma vez, você deve considerar se deseja incluir pontuação como delimitador de uma palavra. A divisão teria em grande parte o mesmo efeito que a solução ingênua de verificar ' is ', notavelmente, ela não pegará This is, a comma'ou 'It is.'.
Bob
7
@JamieBull: Eu duvido que qualquer divisão real de entrada s.split(string.punctuation + string.whitespace)seja dividida uma vez; splitnão é como a família strip/ rstrip/ lstripde funções, apenas se divide quando vê todos os caracteres delimitadores, contiguamente, nessa ordem exata. Se você deseja dividir as classes de caracteres, volta às expressões regulares (nesse ponto, procurar r'\bis\b'sem dividir é o caminho mais simples e rápido).
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, ponto de vista. Isto agora é ridículo ...
Jamie Touro
190

O Python possui uma string que contém o método de substring?

Sim, mas o Python possui um operador de comparação que você deve usar, porque a linguagem pretende seu uso, e outros programadores esperam que você o use. Essa palavra-chave é inusada como um operador de comparação:

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

O oposto (complemento), que a pergunta original pede, é not in:

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

É semanticamente o mesmo, not 'foo' in '**foo**'mas é muito mais legível e explicitamente previsto no idioma como uma melhoria de legibilidade.

Evitar o uso __contains__, findeindex

Como prometido, aqui está o containsmétodo:

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

retorna True. Você também pode chamar esta função a partir da instância da supercorda:

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

Mas não. Os métodos que começam com sublinhados são considerados semanticamente privados. A única razão para usar isso é ao estender a funcionalidade ine not in(por exemplo, se estiver subclassificando 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 agora:

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

Além disso, evite os seguintes métodos de string:

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

Outras linguagens podem não ter métodos para testar diretamente substrings e, portanto, você teria que usar esses tipos de métodos, mas com o Python, é muito mais eficiente usar o inoperador de comparação.

Comparações de desempenho

Podemos comparar várias maneiras de alcançar o mesmo 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'))),
}

E agora vemos que o uso iné muito mais rápido que os outros. Menos tempo para realizar uma operação equivalente é melhor:

>>> 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
fonte
6
Por que alguém deveria evitar str.indexe str.find? De que outra forma você sugeriria que alguém encontrasse o índice de uma substring em vez de apenas existir ou não? (ou você evitar média de usá-los no lugar de contém - portanto, não use s.find(ss) != -1em vez de ss in s?)
coderforlife
3
Precisamente, embora a intenção por trás do uso desses métodos possa ser melhor abordada pelo uso elegante do remódulo. Ainda não encontrei um uso para str.index ou str.find em qualquer código que eu escrevi ainda.
Aaron Hall
Por favor, estenda sua resposta ao conselho contra o uso str.counttambém ( string.count(something) != 0). shudder
cs95
Como funciona a operatorversão do módulo ?
Jpmc26 18/08/19
@ jpmc26 é o mesmo que in_acima - mas com um StackFrame em torno dele, por isso é mais lento do que isso: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall
175

if needle in haystack:é o uso normal, como o @Michael diz - depende do inoperador, mais legível e mais rápido que uma chamada de método.

Se você realmente precisa de um método em vez de um operador (por exemplo, fazer algo estranho key=para um tipo muito peculiar ...?), Isso seria 'haystack'.__contains__. Mas como seu exemplo é para uso em um if, acho que você não está realmente falando sério ;-). Não é uma boa forma (nem legível, nem eficiente) usar métodos especiais diretamente - eles devem ser usados, em vez disso, através dos operadores e componentes internos que os delegam.

Alex Martelli
fonte
55

in Strings e listas Python

Aqui estão alguns exemplos úteis que falam por si sobre o 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]

Embargo. Listas são iteráveis, e o inmétodo atua em iterables, não apenas em strings.

firelynx
fonte
1
A lista iterável poderia ser alternada para procurar qualquer lista em uma única sequência? Ex ["bar", "foo", "foobar"] in "foof":?
precisa
1
@CaffeinatedCoder, não, isso requer iteração aninhada. Melhor feito juntando-se a lista com tubulações "|" .join ([ "bar", "foo", "foobar"]) e compilar um regex fora dele, em seguida, combinando em "foof"
firelynx
2
any ([x em "foof" para x em ["bar", "foo", "foobar"]])
Izaak Weiss
1
@IzaakWeiss Seu liner único funciona, mas não é muito legível e faz iteração aninhada. Eu aconselho a não fazer isso
firelynx
1
@ PiyushS.Wanare o que você quer dizer com complexidade? O "WTF / min" é muito maior com o regex.
firelynx
42

Se você está satisfeito, "blah" in somestringmas deseja que seja uma chamada de função / método, provavelmente poderá fazer isso

import operator

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

Todos os operadores no Python podem ser mais ou menos encontrados no módulo do operador inclusive in.

Jeffrey04
fonte
40

Então, aparentemente, não há nada semelhante para a comparação vetorial. Uma maneira óbvia de Python para fazer isso seria:

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
Ufos
fonte
1
Isso ocorre porque existem inúmeras maneiras de criar um Produto a partir de variáveis ​​atômicas. Você pode colocá-los em uma tupla, em uma lista (que são formas de produtos cartesianos e vêm com uma ordem implícita) ou podem ser nomeadas propriedades de uma classe (sem ordem a priori) ou valores de dicionário, ou podem ser arquivos em um diretório ou qualquer outra coisa. Sempre que você puder identificar (iter ou getitem) algo em um 'contêiner' ou 'contexto', poderá vê-lo como um tipo de vetor e definir operações binárias nele. pt.wikipedia.org/wiki/…
Niriel 10/08/15
Vale nada que innão deva ser usado com listas porque faz uma varredura linear dos elementos e é lento em comparação. Use um conjunto, especialmente se os testes de associação forem feitos repetidamente.
precisa saber é
22

Você pode usar y.count().

Ele retornará o valor inteiro do número de vezes que uma sub string aparecerá em uma string.

Por exemplo:

string.count("bah") >> 0
string.count("Hello") >> 1
Brandon Bailey
fonte
8
contar uma string é caro quando você só quer verificar se existe ...
Jean-François Fabre
3
métodos que existem na postagem original de 2010, então acabei editando-os, com consenso da comunidade (consulte meta post meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre
17
não. Meu argumento é "por que responder exatamente a mesma coisa que os outros fizeram 9 anos atrás"?
Jean-François Fabre
10
porque estou moderando o site ... Fiz a pergunta em meta meta.stackoverflow.com/questions/385063/…
Jean-François Fabre
2
Se você tiver autoridade para removê-lo, remova-o; faça o que for necessário e siga em frente. Na IMO, esta resposta agrega valor, refletido por votos positivos dos usuários.
Brandon Bailey
20

Aqui está a sua resposta:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Para verificar se é falso:

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

OU:

if "insert_char_or_string_here" not in "insert_string_to_search_here":
    #DOSTUFF
ytpillai
fonte
8

Você pode usar expressões regulares para obter as ocorrências:

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