Czy Python ma ciąg „zawiera” metodę podciągów?


3599

Szukam metody string.containslub string.indexofjęzyka Python.

Chcę zrobić:

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

Odpowiedzi:


6257

Możesz użyć inoperatora :

if "blah" not in somestring: 
    continue

230
Pod maską Python użyje __contains__(self, item), __iter__(self)iw __getitem__(self, key)tej kolejności, aby ustalić, czy element leży w danym zawiera. Zaimplementuj co najmniej jedną z tych metod, aby inudostępnić niestandardowy typ.
— BallpointBen,

27
Tylko upewnij się, że coś nie będzie Żaden. W przeciwnym razie dostanieszTypeError: argument of type 'NoneType' is not iterable
— Big Pumpkin

5
FWIW, to idiomatyczny sposób na osiągnięcie tego celu.
— Trenton

6
Czy w przypadku łańcuchów inoperator Python używa algorytmu Rabin-Carp?
— Sam Chats,

3
@SamChats patrz stackoverflow.com/questions/18139660/ ... szczegóły implementacji (w CPython; afaik specyfikacja języka nie wymaga tutaj żadnego konkretnego algorytmu).
— Christoph Burschka

667

Jeśli jest to tylko wyszukiwanie podciągów, możesz użyć string.find("substring").

Trzeba być trochę ostrożny z find, indexi inchoć są one podciąg wyszukiwania. Innymi słowy, to:

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

Wypisałby się Found 'is' in the string.podobnie, if "is" in s:oceniałby True. To może być lub nie być to, czego chcesz.


78
+1 za wyróżnienie błędów związanych z wyszukiwaniem podciągów. oczywistym rozwiązaniem jest to, if ' is ' in s:co powróci, Falsejak się spodziewano (prawdopodobnie).
— aaronasterling

94
@aaronasterling Oczywiście może być, ale nie do końca poprawny. Co zrobić, jeśli masz interpunkcję lub jest ona na początku lub na końcu? Co z dużymi literami? Lepszym byłoby wyszukiwanie wyrażeń regularnych bez rozróżniania wielkości liter \bis\b(granice słów).
— Bob

2
@JamieBull Jeszcze raz musisz rozważyć, czy chcesz wstawić interpunkcję jako separator słowa. Podział miałby w dużej mierze taki sam efekt, jak naiwne rozwiązanie sprawdzania ' is ', w szczególności, że nie zostanie on złapany This is, a comma'lub 'It is.'.
— Bob

7
@JamieBull: Wątpię, czy jakikolwiek prawdziwy podział danych wejściowych podzieliłby się s.split(string.punctuation + string.whitespace)choć raz; splitnie jest jak rodzina funkcji strip/ rstrip/ lstrip, dzieli się tylko wtedy, gdy widzi wszystkie znaki separatora, w sposób ciągły, w dokładnie takiej kolejności. Jeśli chcesz podzielić na klasy znaków, wrócisz do wyrażeń regularnych (w tym momencie wyszukiwanie r'\bis\b'bez podziału jest łatwiejszą i szybszą drogą).
— 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, punkt wzięty. To jest teraz śmieszne ...
— Jamie Bull

190

Czy Python ma ciąg zawierający metodę podciągów?

Tak, ale Python ma operator porównania, którego należy użyć zamiast tego, ponieważ język zamierza go używać, a inni programiści oczekują, że będziesz go używać. To słowo kluczowe jest inużywane jako operator porównania:

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

Przeciwnie (uzupełnienie), o które prosi pierwotne pytanie, jest not in:

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

Jest to semantycznie to samo, not 'foo' in '**foo**'ale o wiele bardziej czytelne i wyraźnie przewidziane w języku jako poprawa czytelności.

Unikaj używania __contains__, findorazindex

Zgodnie z obietnicą, oto containsmetoda:

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

zwraca True. Możesz także wywołać tę funkcję z instancji superstringu:

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

Ale nie rób tego. Metody rozpoczynające się od podkreślenia są uważane za semantycznie prywatne. Jedynym powodem, dla którego można to wykorzystać, jest rozszerzenie funkcji ini not in(np. W przypadku podklasy 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')

i teraz:

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

Unikaj również następujących metod ciągu:

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

Inne języki mogą nie mieć żadnych metod bezpośredniego testowania podciągów, więc musisz użyć tego rodzaju metod, ale w Pythonie znacznie bardziej wydajne jest użycie inoperatora porównania.

Porównanie wydajności

Możemy porównać różne sposoby osiągnięcia tego samego celu.

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

A teraz widzimy, że używanie injest znacznie szybsze niż inne. Im mniej czasu na wykonanie równoważnej operacji, tym lepiej:

>>> 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
Dlaczego należy unikać str.indexi str.find? Jak inaczej zasugerowałbyś komuś znalezienie indeksu podciągów zamiast tego, czy istnieje? (czy miałeś na myśli unikanie ich zamiast zawiera - więc nie używaj s.find(ss) != -1zamiast ss in s?)
— coderforlife

3
Dokładnie tak, chociaż zamiar zastosowania tych metod można lepiej rozwiązać poprzez eleganckie użycie remodułu. Nie znalazłem jeszcze zastosowania dla str.index lub str.find siebie w żadnym kodzie, który napisałem.
— Aaron Hall

Rozszerz swoją odpowiedź na odradzanie używania str.countrównież ( string.count(something) != 0). Dreszcz
— CS95


@ jpmc26 jest taki sam jak in_powyżej - ale z ramką stosu, więc jest wolniejszy: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
— Aaron Hall

175

if needle in haystack:jest normalnym zastosowaniem, jak mówi @Michael - zależy od inoperatora, jest bardziej czytelny i szybszy niż wywołanie metody.

Jeśli naprawdę potrzebujesz metody zamiast operatora (np. Aby zrobić coś dziwnego key=dla bardzo osobliwego rodzaju ...?), Byłoby to możliwe 'haystack'.__contains__. Ale ponieważ twój przykład jest przeznaczony do użycia if, myślę, że tak naprawdę nie masz na myśli tego, co mówisz ;-). Nie jest dobrą formą (ani czytelną, ani wydajną) używanie specjalnych metod bezpośrednio - są one przeznaczone do użycia przez operatorów i wbudowane w nie funkcje, które je delegują.


55

in Ciągi i listy w języku Python

Oto kilka przydatnych przykładów, które mówią same za siebie w odniesieniu do inmetody:

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

Zastrzeżenie Listy są iterowalne, a inmetoda działa na iterowalne, a nie tylko na ciągi znaków.


1
Czy iterowalną listę można przełączyć, aby wyszukać dowolną listę w jednym ciągu? Ex: ["bar", "foo", "foobar"] in "foof"?
— CaffeinatedCoder,

1
@CaffeinatedCoder, nie, wymaga to zagnieżdżonej iteracji. Najlepiej zrobić to, łącząc listę z potokami „|” .join ([„bar”, „foo”, „foobar”]) i kompilując z niej wyrażenie regularne, a następnie dopasowując „foof”
— firelynx

2
dowolna ([x w „foof” dla x w [„bar”, „foo”, „foobar”]])
— Izaak Weiss

1
@IzaakWeiss Twój jeden liner działa, ale nie jest zbyt czytelny i zagnieżdża iterację.
— Odradzałbym

1
@ PiyushS. Czy rozumiesz, co rozumiesz przez złożoność? „WTF / min” jest znacznie wyższy w przypadku wyrażenia regularnego.
— firelynx,

42

Jeśli jesteś zadowolony, "blah" in somestringale chcesz, aby było to wywołanie funkcji / metody, prawdopodobnie możesz to zrobić

import operator

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

Wszystkich operatorów w Pythonie można mniej więcej znaleźć w module operatora, w tym in.


40

Najwyraźniej więc nie ma nic podobnego w porównaniu wektorowym. Oczywistym sposobem na zrobienie tego byłoby:

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
Jest tak, ponieważ istnieją bajillionowe sposoby tworzenia produktu ze zmiennych atomowych. Możesz upchnąć je w krotkę, listę (które są formami produktów kartezjańskich i są dostarczane z dorozumianą kolejnością), lub można je nazwać właściwościami klasy (bez zamówienia a priori) lub wartościami słownikowymi, lub mogą być plikami katalog lub cokolwiek innego. Ilekroć możesz jednoznacznie zidentyfikować (iterować lub uzyskać) coś w „kontenerze” lub „kontekście”, możesz zobaczyć ten „kontener” jako rodzaj wektora i zdefiniować na nim operacje binarne. en.wikipedia.org/wiki/…
— Niriel,

Nie warto nic, inczego nie należy używać z listami, ponieważ wykonuje liniowy skan elementów i jest powolny w porównaniu. Zamiast tego użyj zestawu, szczególnie jeśli testy członkostwa mają być powtarzane.
— cs95

22

Możesz użyć y.count().

Zwraca wartość całkowitą liczby przypadków, gdy podciąć pojawia się w ciągu.

Na przykład:

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

7
liczenie łańcucha jest kosztowne, gdy chcesz po prostu sprawdzić, czy tam jest ...
— Jean-François Fabre

3
metody, które istnieją w oryginalnym poście z 2010 roku, więc skończyłem je edytować, za zgodą społeczności (patrz meta post meta.stackoverflow.com/questions/385063/... )
— Jean-François Fabre

17
Nie. Chodzi mi o to „po co odpowiadać dokładnie tak samo, jak inni 9 lat temu”?
— Jean-François Fabre


2
następnie Jeśli masz uprawnienia do usunięcia go, usuń go, w przeciwnym razie zrób to, co musisz, i przejdź dalej. IMO ta odpowiedź dodaje wartości, co znajduje odzwierciedlenie w głosowaniu użytkowników.
— Brandon Bailey

20

Oto twoja odpowiedź:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Aby sprawdzić, czy jest to fałsz:

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

LUB:

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

8

Możesz użyć wyrażeń regularnych, aby uzyskać wystąpienia:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.