Есть ли в Python метод "содержит" подстроку для строки?

3599

Я ищу string.containsили string.indexofметод в Python.

Я хочу сделать:

if not somestring.contains("blah"):
   continue
Бланкмэн
источник

Ответы:

6266

Вы можете использовать inоператор :

if "blah" not in somestring: 
    continue
Майкл Мрозек
источник
233
Под капотом Python будет использовать __contains__(self, item), __iter__(self)и __getitem__(self, key)в этом порядке, чтобы определить, находится ли элемент в данном содержимом. Реализуйте хотя бы один из этих методов, чтобы сделать его inдоступным для вашего пользовательского типа.
BallpointBen
28
Просто убедитесь, что не будет None. В противном случае вы получитеTypeError: argument of type 'NoneType' is not iterable
Большая тыква
6
FWIW, это идиоматический способ достижения указанной цели.
Трентон
7
Для строк использует ли inоператор Python алгоритм Рабина-Карпа?
Сэм Чаты
4
@SamChats см stackoverflow.com/questions/18139660/... для деталей реализации (в CPython, AFAIK спецификация языка не предписывает какой - либо конкретный алгоритм здесь).
Кристоф Буршка
667

Если это просто поиск по подстроке, вы можете использовать string.find("substring").

Вы должны быть немного осторожным с find, indexи inхотя, как они поиск подстрок. Другими словами, это:

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

Точно Found 'is' in the string.так же вывел if "is" in s:бы, оценил бы True. Это может или не может быть то, что вы хотите.

eldarerathis
источник
78
+1 за выделение ошибок, связанных с поиском подстроки. очевидное решение состоит в том, if ' is ' in s:который вернется так, Falseкак (вероятно) ожидается.
Ааронастерлинг
95
@aaronasterling Очевидно, что это может быть, но не совсем правильно. Что если у вас есть пунктуация или она в начале или в конце? Как насчет капитализации? Лучше было бы поиск по регулярному выражению без учета регистра \bis\b(границы слова).
Боб
2
@JamieBull Еще раз, вы должны подумать, если вы хотите включить пунктуацию в качестве разделителя для слова. Расщепление будет иметь в значительной степени тот же эффект, что и наивное решение проверки ' is ', в частности, оно не поймает This is, a comma'или 'It is.'.
Боб
7
@JamieBull: я очень сомневаюсь, что реальное разделение входных данных s.split(string.punctuation + string.whitespace)разделит хотя бы один раз; splitэто не то же самое, что семейство функций strip/ rstrip/ lstrip, он разделяется только тогда, когда видит все символы-разделители, непрерывно, в том же порядке. Если вы хотите разделить классы символов, вы вернетесь к регулярным выражениям (в этот момент поиск r'\bis\b'без разделения - более простой и быстрый путь).
ShadowRanger
8
'is' not in (w.lower() for w in s.translate(string.maketrans(' ' * len(string.punctuation + string.whitespace), string.punctuation + string.whitespace)).split()- Хорошо, точка взята. Теперь это смешно ...
Джейми Булл,
190

Есть ли в Python строка, содержащая метод подстроки?

Да, но в Python есть оператор сравнения, который вы должны использовать вместо этого, потому что язык предполагает его использование, и другие программисты будут ожидать, что вы будете его использовать. Это ключевое слово in, которое используется в качестве оператора сравнения:

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

Противоположность (дополнение), о которой просит исходный вопрос not in:

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

Семантически это то же самое, not 'foo' in '**foo**'что и гораздо более читабельно и явно предусмотрено в языке как улучшение читаемости.

Избегайте использования __contains__, findиindex

Как и было обещано, вот containsметод:

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

возвращается True. Вы также можете вызвать эту функцию из экземпляра суперструны:

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

Но не надо. Методы, которые начинаются с подчеркивания, считаются семантически закрытыми. Единственная причина , чтобы использовать это при расширении inи not inфункциональные возможности (например , если подклассов 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')

и сейчас:

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

Также избегайте следующих строковых методов:

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

Другие языки могут не иметь методов для непосредственного тестирования подстрок, и поэтому вам придется использовать эти типы методов, но с Python гораздо эффективнее использовать inоператор сравнения.

Сравнение производительности

Мы можем сравнить различные способы достижения одной и той же цели.

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

И теперь мы видим, что использование inнамного быстрее, чем другие. Лучше меньше времени на выполнение эквивалентной операции:

>>> 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
Почему следует избегать str.indexи str.find? Как еще вы могли бы предложить кому-то найти индекс подстроки вместо того, существует ли он или нет? (или вы имели в виду избегать их использования s.find(ss) != -1вместо ss in s
содержимого
3
Именно так, хотя цель использования этих методов может быть лучше решена путем элегантного использования reмодуля. Я еще не нашел использования str.index или str.find себя ни в одном коде, который я написал.
Аарон Холл
Пожалуйста, распространите свой ответ на совет против использования str.count( string.count(something) != 0). дрожь
cs95
Как работает operatorверсия модуля ?
jpmc26
@ jpmc26 - это то же самое, что и in_выше - но со стековым фреймом вокруг него, поэтому оно медленнее: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Аарон Холл
175

if needle in haystack:как говорит @Michael, это обычное использование - оно опирается на inоператор, более читабельное и более быстрое, чем вызов метода.

Если вам действительно нужен метод вместо оператора (например, чтобы сделать какой-то странный key=для очень своеобразного рода ...?), Это было бы 'haystack'.__contains__. Но так как ваш пример для использования в if, я думаю, вы на самом деле не имеете в виду то, что говорите ;-). Непригодно (ни читабельно, ни эффективно) напрямую использовать специальные методы - они предназначены для использования вместо них через операторы и встроенные функции, которые им делегируют.

Алекс Мартелли
источник
55

in Python строки и списки

Вот несколько полезных примеров, которые говорят сами за себя относительно inметода:

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

Предостережение. Списки являются итеративными, а inметод действует на итерируемые, а не только на строки.

firelynx
источник
1
Можно ли переключить итеративный список для поиска любого списка в одной строке? Пример: ["bar", "foo", "foobar"] in "foof"?
CaffeinatedCoder
1
@CaffeinatedCoder, нет, это требует вложенной итерации. Лучше всего это сделать, объединив список с помощью каналов "|" .join (["bar", "foo", "foobar"]) и скомпилировав из него регулярное выражение, а затем сопоставив его с "foof"
firelynx
2
любой ([x в "foof" для x в ["bar", "foo", "foobar"]])
Исаак Вайс
1
@IzaakWeiss Ваш один вкладыш работает, но он не очень читабелен и выполняет вложенные итерации. Я бы посоветовал не делать этого
firelynx
1
@ PiyushS. Что вы подразумеваете под сложностью? «WTF / мин» намного выше с регулярным выражением.
firelynx
42

Если вы довольны, "blah" in somestringно хотите, чтобы это был вызов функции / метода, вы, вероятно, можете сделать это

import operator

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

Все операторы в Python могут быть более или менее найдены в модуле оператора, включая in.

Jeffrey04
источник
40

Поэтому, очевидно, нет ничего похожего для векторного сравнения. Очевидный способ сделать это на Python:

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
источник
1
Это потому, что существует множество способов создания Продукта из атомарных переменных. Вы можете поместить их в кортеж, список (которые являются формами декартовых произведений и имеют подразумеваемый порядок), или они могут быть названы свойствами класса (без априорного порядка) или значениями словаря, или они могут быть файлами в каталог или что-то еще. Всякий раз, когда вы можете однозначно идентифицировать (iter или getitem) что-либо в «контейнере» или «контексте», вы можете видеть этот «контейнер» как своего рода вектор и определять двоичные операции для него. en.wikipedia.org/wiki/…
Нириэль
Ничего inне стоит использовать со списками, потому что он выполняет линейное сканирование элементов и сравнивается медленно. Вместо этого используйте набор, особенно если тесты на членство должны выполняться повторно.
cs95
22

Вы можете использовать y.count().

Он вернет целочисленное значение числа раз, когда подстрока появляется в строке.

Например:

string.count("bah") >> 0
string.count("Hello") >> 1
Брэндон Бэйли
источник
8
подсчет строки стоит дорого, если вы просто хотите проверить, есть ли она ...
Жан-Франсуа Фабр
3
методы, которые существуют в оригинальном посте от 2010 года, поэтому я закончил редактировать их, с консенсусом сообщества (см. мета-пост meta.stackoverflow.com/questions/385063/… )
Жан-Франсуа Фабр
17
нет. Я хочу сказать: «Почему отвечать так же, как другие 9 лет назад»?
Жан-Франсуа Фабр
10
потому что я модерирую сайт ... Я задал вопрос на meta meta.stackoverflow.com/questions/385063/…
Жан-Франсуа Фабр
2
затем, если у вас есть полномочия удалить его, удалите его, в противном случае сделайте то, что должны, и продолжайте. ИМО этот ответ добавляет ценность, что отражается в положительных откликах пользователей.
Брэндон Бэйли
20

Вот ваш ответ:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Для проверки, является ли это ложным:

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

ИЛИ:

if "insert_char_or_string_here" not in "insert_string_to_search_here":
    #DOSTUFF
ytpillai
источник
8

Вы можете использовать регулярные выражения, чтобы получить вхождения:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Muskovets
источник