Apakah Python memiliki string 'berisi' metode substring?

3599

Saya mencari string.containsatau string.indexofmetode dengan Python.

Aku ingin melakukan:

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

Jawaban:

6266

Anda dapat menggunakan inoperator :

if "blah" not in somestring: 
    continue
Michael Mrozek
sumber
232
Di bawah tenda, Python akan menggunakan __contains__(self, item),, __iter__(self)dan __getitem__(self, key)dalam urutan itu untuk menentukan apakah suatu item terletak pada suatu konten. Terapkan setidaknya satu dari metode itu untuk membuat intersedia untuk jenis kustom Anda.
BallpointBen
27
Pastikan saja bahwa somestring tidak akan menjadi None. Jika tidak, Anda akan mendapatkanTypeError: argument of type 'NoneType' is not iterable
Labu Besar
5
FWIW, ini adalah cara idiomatis untuk mencapai tujuan tersebut.
Trenton
6
Untuk string, apakah inoperator Python menggunakan algoritma Rabin-Carp?
Sam Chats
3
@ SamChats, lihat stackoverflow.com/questions/18139660/... untuk detail implementasi (dalam CPython; afaik spesifikasi bahasa tidak mengharuskan algoritma tertentu di sini).
Christoph Burschka
667

Jika itu hanya pencarian substring yang dapat Anda gunakan string.find("substring").

Anda harus sedikit berhati-hati dengan find, index, dan inmeskipun, seperti yang substring pencarian. Dengan kata lain, ini:

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

Itu akan mencetak sama Found 'is' in the string., if "is" in s:akan mengevaluasi True. Ini mungkin atau mungkin bukan yang Anda inginkan.

eldarerathis
sumber
78
+1 untuk menyorot gotcha yang terlibat dalam pencarian substring. solusi yang jelas adalah if ' is ' in s:yang akan kembali Falseseperti yang diharapkan (mungkin).
aaronasterling
95
@ aaronasterling Jelas itu mungkin, tetapi tidak sepenuhnya benar. Bagaimana jika Anda memiliki tanda baca atau itu pada awal atau akhir? Bagaimana dengan kapitalisasi? Lebih baik akan menjadi kasus pencarian regex tidak sensitif untuk \bis\b(batas kata).
Bob
2
@JamieBull Sekali lagi, Anda harus mempertimbangkan jika Anda ingin memasukkan tanda baca sebagai pembatas untuk sebuah kata. Pemisahan sebagian besar akan memiliki efek yang sama dengan solusi naif untuk memeriksa ' is ', terutama, itu tidak akan menangkap This is, a comma'atau 'It is.'.
Bob
7
@ JamieBull: Saya sangat meragukan setiap input split nyata dengan s.split(string.punctuation + string.whitespace)akan membagi bahkan sekali; splittidak seperti strip/ rstrip/ lstripkeluarga fungsi, itu hanya membagi ketika melihat semua karakter pembatas, contiguously, dalam urutan yang tepat. Jika Anda ingin membagi pada kelas karakter, Anda kembali ke ekspresi reguler (pada titik itu, mencari r'\bis\b'tanpa pemisahan adalah cara yang lebih sederhana, lebih cepat untuk pergi).
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, poin diambil. Ini sekarang konyol ...
Jamie Bull
190

Apakah Python memiliki string yang berisi metode substring?

Ya, tetapi Python memiliki operator pembanding yang harus Anda gunakan sebagai gantinya, karena bahasa tersebut bermaksud penggunaannya, dan programmer lain akan mengharapkan Anda untuk menggunakannya. Kata kunci itu in, yang digunakan sebagai operator perbandingan:

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

Sebaliknya (komplemen), yang ditanyakan oleh pertanyaan asli, adalah not in:

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

Ini semantik sama dengan not 'foo' in '**foo**'tetapi jauh lebih mudah dibaca dan secara eksplisit disediakan dalam bahasa sebagai peningkatan keterbacaan.

Hindari menggunakan __contains__, finddanindex

Seperti yang dijanjikan, inilah containscaranya:

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

kembali True. Anda juga dapat memanggil fungsi ini dari instance superstring:

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

Tapi jangan. Metode yang dimulai dengan garis bawah dianggap semantik pribadi. Satu-satunya alasan untuk menggunakan ini adalah ketika memperluas indan not infungsionalitas (mis. Jika subclassing 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')

dan sekarang:

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

Selain itu, hindari metode string berikut:

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

Bahasa lain mungkin tidak memiliki metode untuk menguji langsung untuk substring, dan Anda harus menggunakan jenis metode ini, tetapi dengan Python, jauh lebih efisien untuk menggunakan inoperator pembanding.

Perbandingan kinerja

Kita dapat membandingkan berbagai cara untuk mencapai tujuan yang sama.

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

Dan sekarang kita melihat bahwa menggunakan injauh lebih cepat daripada yang lain. Lebih sedikit waktu untuk melakukan operasi yang setara lebih baik:

>>> 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
sumber
6
Mengapa orang harus menghindari str.indexdan str.find? Bagaimana lagi Anda menyarankan seseorang menemukan indeks substring alih-alih hanya apakah itu ada atau tidak? (Atau maksud Anda menghindari menggunakannya di tempat mengandung - jadi jangan gunakan s.find(ss) != -1bukan ss in s?)
coderforlife
3
Tepatnya, meskipun maksud di balik penggunaan metode-metode itu mungkin lebih baik diatasi dengan penggunaan remodul yang elegan . Saya belum menemukan penggunaan untuk str.index atau str.find sendiri dalam kode apa pun yang saya tulis.
Aaron Hall
Harap rentangkan jawaban Anda ke saran yang menentang penggunaan str.countjuga ( string.count(something) != 0). gemetar
cs95
Bagaimana kinerja operatorversi modul ?
jpmc26
@ jpmc26 sama seperti di in_atas - tetapi dengan susunan bingkai di sekitarnya, jadi lebih lambat dari itu: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall
175

if needle in haystack:adalah penggunaan normal, seperti yang dikatakan @Michael - ini bergantung pada inoperator, lebih mudah dibaca dan lebih cepat daripada pemanggilan metode.

Jika Anda benar-benar membutuhkan metode alih-alih operator (mis. Untuk melakukan sesuatu yang aneh key=untuk jenis yang sangat aneh ...?), Itu mungkin 'haystack'.__contains__. Tapi karena contoh Anda adalah untuk digunakan dalam if, saya kira Anda tidak benar-benar bermaksud apa yang Anda katakan ;-). Ini bukan bentuk yang baik (atau dapat dibaca, atau efisien) untuk menggunakan metode khusus secara langsung - mereka seharusnya digunakan, sebagai gantinya, melalui operator dan builtin yang mendelegasikan kepada mereka.

Alex Martelli
sumber
55

in String dan daftar Python

Berikut adalah beberapa contoh berguna yang berbicara sendiri tentang inmetode ini:

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

Peringatan. Daftar adalah iterables, dan inmetode ini bekerja pada iterables, bukan hanya string.

firelynx
sumber
1
Mungkinkah daftar itu diubah untuk mencari daftar dalam satu string? Mis .: ["bar", "foo", "foobar"] in "foof"?
CaffeinatedCoder
1
@CaffeinatedCoder, tidak, ini membutuhkan iterasi bersarang. Paling baik dilakukan dengan bergabung dengan daftar dengan pipa "|" .join (["bar", "foo", "foobar"]) dan menyusun regex, lalu mencocokkannya dengan "foof"
firelynx
2
any ([x dalam "foof" untuk x di ["bar", "foo", "foobar"]])
Izaak Weiss
1
@IzaakWeiss Satu liner Anda berfungsi, tetapi tidak terlalu mudah dibaca, dan iterasi bersarang. Saya akan menyarankan untuk tidak melakukan ini
firelynx
1
@ PiyushS.Wanare apa yang Anda maksud dengan kompleksitas? "WTF / mnt" jauh lebih tinggi dengan regex.
firelynx
42

Jika Anda senang "blah" in somestringtetapi menginginkannya menjadi panggilan fungsi / metode, Anda mungkin dapat melakukan ini

import operator

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

Semua operator di Python dapat lebih atau kurang ditemukan dalam modul operator termasuk in.

Jeffrey04
sumber
40

Jadi rupanya tidak ada yang serupa untuk perbandingan bijaksana. Cara Python yang jelas untuk melakukannya adalah:

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
sumber
1
Itu karena ada bajillion cara menciptakan Produk dari variabel atom. Anda dapat memasukkannya dalam tuple, daftar (yang merupakan bentuk Produk Cartesian dan disertai pesanan tersirat), atau mereka dapat dinamai properti kelas (tanpa urutan apriori) atau nilai kamus, atau dapat berupa file dalam direktori, atau apa pun. Setiap kali Anda dapat secara unik mengidentifikasi (iter atau getitem) sesuatu dalam 'wadah' atau 'konteks', Anda dapat melihat 'wadah' itu sebagai semacam vektor dan mendefinisikan operasi biner di atasnya. en.wikipedia.org/wiki/…
Niriel
Layak apa-apa yang intidak boleh digunakan dengan daftar karena melakukan pemindaian linear elemen dan lambat dibandingkan. Gunakan set sebagai gantinya, terutama jika tes keanggotaan harus dilakukan berulang kali.
cs95
22

Anda bisa menggunakannya y.count().

Ini akan mengembalikan nilai integer dari berapa kali sebuah sub string muncul dalam sebuah string.

Sebagai contoh:

string.count("bah") >> 0
string.count("Hello") >> 1
Brandon Bailey
sumber
8
menghitung string adalah mahal ketika Anda hanya ingin memeriksa apakah itu ada ...
Jean-François Fabre
3
metode yang ada di pos asli dari 2010 jadi saya akhirnya mengeditnya, dengan konsensus dari komunitas (lihat meta post meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre
17
tidak. Maksud saya adalah "mengapa menjawab hal yang sama persis seperti yang dilakukan orang lain 9 tahun lalu"?
Jean-François Fabre
10
karena saya memoderasi situs ... Saya telah mengajukan pertanyaan pada meta meta.stackoverflow.com/questions/385063/…
Jean-François Fabre
2
kemudian Jika Anda memiliki wewenang untuk menghapusnya kemudian menghapusnya, lakukan apa yang harus Anda lakukan dan lanjutkan. IMO jawaban ini menambah nilai, yang dicerminkan oleh suara terbanyak dari pengguna.
Brandon Bailey
20

Inilah jawaban Anda:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Untuk memeriksa apakah itu salah:

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

ATAU:

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

Anda dapat menggunakan ekspresi reguler untuk mendapatkan kemunculan:

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