Чи є в Python рядок "містить" метод підрядки?


3599

Я шукаю string.containsабо string.indexofметод у Python.

Я хочу зробити:

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

Відповіді:


6257

Ви можете скористатися inоператором :

if "blah" not in somestring: 
    continue

230
Під капотом Python буде використовувати __contains__(self, item), __iter__(self)і __getitem__(self, key)в цьому порядку визначати, чи лежить елемент у заданому вмісті. Реалізуйте принаймні один із цих методів, щоб зробити inдоступними для вашого власного типу.
BallpointBen

27
Просто переконайтесь, що деяка нитка не буде None. В іншому випадку ви отримаєтеTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin

5
FWIW, це ідіоматичний спосіб досягнення зазначеної мети.
Трентон

6
Чи використовує рядки inоператор Python для алгоритму Rabin-Carp?
Сем Чат

3
@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. Це може бути або не бути тим, що ви хочете.


78
+1 для виділення ґетча, що бере участь у пошуку підрядків. очевидним рішенням є те, if ' is ' in s:що повернеться так, Falseяк це (мабуть) очікується.
aaronasterling

94
@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?)
coderforlife

3
Саме так, хоча намір використання цих методів може бути краще вирішений елегантним використанням reмодуля. Я ще не знайшов використання str.index або str.find у будь-якому коді, який я ще написав.
Аарон Холл

Будь ласка, поширіть свою відповідь на пораду щодо використання str.countтакож ( string.count(something) != 0). здригаються
cs95


@ jpmc26 - це те саме, що in_вище, але зі складовою рамкою навколо нього, тож це повільніше, ніж це: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall

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]

Caveat. Списки є ітерабельними, а inметод діє на ітерабелі, а не лише на рядки.


1
Чи можна переключити ітерабельний список, щоб шукати будь-який зі списку в одному рядку? Наприклад ["bar", "foo", "foobar"] in "foof":?
CaffeinatedCoder

1
@CaffeinatedCoder, ні, для цього потрібна вкладена ітерація. Найкраще зробити це, приєднавшись до списку з трубами "|" .join (["бар", "foo", "foobar"]) і склавши з нього регулярний вираз, потім співпадаючи з "foof"
firelynx

2
будь-який ([x in "foof" for x in ["bar", "foo", "foobar"]])
Ізаак Вайс

1
@IzaakWeiss Ваш один вкладиш працює, але він не дуже читабельний, і він робить вкладену ітерацію. Я б
радив

1
@ PiyushS.Wanare Що ви маєте на увазі під складністю? "WTF / хв" набагато вище з регулярним виразом.
firelynx

42

Якщо ви задоволені, "blah" in somestringале хочете, щоб це був виклик функції / методу, ви, ймовірно, можете це зробити

import operator

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

Усі оператори в Python можуть бути більш-менш знайдені в операторському модулі, включаючи in.


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

1
Це тому, що існує мільярд способів створення Продукту з атомних змінних. Ви можете їх заповнити в кортежі, списку (які є декартовими продуктами і мають на увазі замовлення), або вони можуть бути названі властивостями класу (без апріорного порядку) або значеннями словника, або вони можуть бути файлами в каталог, або що завгодно. Щоразу, коли ви можете однозначно ідентифікувати (iter або getitem) щось у "контейнері" або "контексті", ви можете бачити цей "контейнер" як своєрідний вектор і визначати на ньому бінарні опси. en.wikipedia.org/wiki/…
Нірієль

Не варто нічого inзастосовувати зі списками, оскільки це робить лінійне сканування елементів і порівняно повільне. Замість цього використовуйте набір, особливо якщо тести на членство потрібно робити неодноразово.
cs95

22

Можна використовувати y.count().

Він поверне ціле значення кількості разів, коли в рядку з'являється підряд.

Наприклад:

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

8
підрахунок рядка коштує дорого, коли ви просто хочете перевірити, чи він там є ...
Жан-Франсуа Фабре

3
методи, які існують у початковій публікації з 2010 року, тому я закінчив редагувати їх, домовляючись спільноти (див. meta post meta.stackoverflow.com/questions/385063/… )
Жан-Франсуа Фабре

17
ні. Моя думка - "чому відповідати точно так само, як інші 9 років тому"?
Жан-Франсуа Фабре


2
тоді Якщо у вас є повноваження його видалити, тоді видаліть його, інакше зробіть все, що вам потрібно, і рухайтеся далі. Ця відповідь IMO додає цінності, що відображається голосуванням користувачів.
Брендон Бейлі

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

8

Ви можете використовувати регулярні вирази для отримання подій:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.