Python มีสตริง 'มี' วิธีการย่อยหรือไม่?


3599

ฉันกำลังมองหาstring.containsหรือstring.indexofวิธีการในหลาม

ฉันต้องการทำ:

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

คำตอบ:


6259

คุณสามารถใช้inโอเปอเรเตอร์ :

if "blah" not in somestring: 
    continue

231
ภายใต้ประทุน, Python จะใช้__contains__(self, item), __iter__(self)และ__getitem__(self, key)อยู่ในลำดับที่เพื่อตรวจสอบว่ารายการโกหกในที่กำหนดให้มี ใช้วิธีการเหล่านี้อย่างน้อยหนึ่งวิธีเพื่อให้inสามารถใช้ได้กับประเภทที่คุณกำหนดเอง
BallpointBen

27
เพียงตรวจสอบให้แน่ใจว่าการปิดบังจะไม่เป็นไม่มีเลย มิฉะนั้นคุณจะได้รับTypeError: argument of type 'NoneType' is not iterable
ฟักทองขนาดใหญ่

5
FWIW นี่เป็นวิธีที่ใช้สำนวนเพื่อบรรลุเป้าหมายดังกล่าว
เทรนตัน

6
สำหรับสตริงตัวinดำเนินการPython ใช้อัลกอริทึม Rabin-Carp หรือไม่
Sam Chats

3
@SamChats ดูstackoverflow.com/questions/18139660/…สำหรับรายละเอียดการใช้งาน (ใน CPython; Afaik ข้อกำหนดภาษาไม่ได้บังคับขั้นตอนวิธีเฉพาะใด ๆ ที่นี่)
Christoph Burschka

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 สำหรับการเน้น gotchas ที่เกี่ยวข้องกับการค้นหาสตริงย่อย ทางออกที่ชัดเจนคือif ' is ' in s:สิ่งที่จะกลับมาFalseตามที่คาดไว้
aaronasterling

95
@aaronasterling เห็นได้ชัดว่าอาจเป็นไปได้ แต่ไม่ถูกต้องทั้งหมด ถ้าคุณมีเครื่องหมายวรรคตอนหรือตอนเริ่มต้นหรือสิ้นสุด การใช้ตัวพิมพ์ใหญ่คืออะไร ดีกว่าจะเป็นกรณีการค้นหา regex ตาย\bis\b(ขอบเขตคำ)
บ๊อบ

2
@JamieBull อีกครั้งคุณต้องพิจารณาว่าคุณต้องการใส่เครื่องหมายวรรคตอนเป็นตัวคั่นคำหรือไม่ แยกส่วนใหญ่จะมีผลเช่นเดียวกับการแก้ปัญหาไร้เดียงสาของการตรวจสอบสำหรับ' is 'สะดุดตาก็จะไม่จับหรือThis is, a comma' 'It is.'
Bob

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ผลตอบแทน คุณสามารถเรียกใช้ฟังก์ชันนี้ได้จากอินสแตนซ์ของ superstring:

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

แต่ทำไม่ได้ วิธีการที่ขึ้นต้นด้วยเครื่องหมายขีดล่างถือว่าเป็นความหมายแบบส่วนตัว เหตุผลเดียวที่จะใช้สิ่งนี้คือเมื่อขยายinและnot inฟังก์ชั่น (เช่นถ้า 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')

และตอนนี้:

>>> '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 ตัวเองในรหัสใด ๆ ที่ฉันได้เขียนเลย
Aaron Hall

โปรดขยายคำตอบของคุณเพื่อให้คำแนะนำกับการใช้str.countเช่นกัน ( string.count(something) != 0) ตัวสั่น
cs95

วิธีไม่operatorรุ่นโมดูลดำเนินการ?
jpmc26

@ jpmc26 เป็นเช่นเดียวกับin_ข้างบน - แต่ด้วย stackframe รอบ ๆ ดังนั้นจึงช้ากว่านั้น: 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]

ข้อแม้. รายการเป็นแบบ iterables และinวิธีการทำงานกับ iterables ไม่ใช่แค่สตริง


1
สามารถสลับรายการที่สามารถทำซ้ำได้เพื่อค้นหารายการใด ๆ ในสตริงเดียวได้หรือไม่? Ex: ["bar", "foo", "foobar"] in "foof"?
CaffeinatedCoder

1
@CaffeinatedCoder ไม่ต้องใช้การซ้ำซ้อนแบบซ้อนกัน ทำได้ดีที่สุดโดยการเข้าร่วมรายการกับท่อ "|" .join (["บาร์", "foo", "foobar"]) และรวบรวม regex ออกจากนั้นจึงจับคู่กับ "foof"
f allnx

2
ใด ๆ ([x ใน "foof" สำหรับ x ใน ["บาร์", "foo", "foobar"]]))
Izaak Weiss

1
@IzaakWeiss สายการบินหนึ่งของคุณทำงานได้ แต่มันไม่สามารถอ่านได้มากและมันซ้ำซ้อนแบบซ้อนกัน ฉันอยากจะให้คำแนะนำกับการทำเช่นนี้
2560

1
@ PiyushS.Wanare คุณหมายถึงความซับซ้อนอย่างไร? "WTF / นาที" สูงกว่ามากด้วย regex
fAllnx

42

หากคุณมีความสุข"blah" in somestringแต่อยากให้มันเป็นการเรียกฟังก์ชั่น / เมธอดคุณอาจทำได้

import operator

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

ผู้ประกอบการทั้งหมดในหลามได้มากขึ้นหรือน้อยกว่าที่พบในโมดูลผู้ประกอบการinรวมทั้ง


40

เห็นได้ชัดว่าไม่มีอะไรคล้ายกันสำหรับการเปรียบเทียบเวกเตอร์ที่ชาญฉลาด วิธีที่ชัดเจนของงูหลามคือ:

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
นั่นเป็นเพราะมีวิธีมากมายในการสร้างผลิตภัณฑ์จากตัวแปรอะตอม คุณสามารถเก็บไว้ใน tuple รายการ (ซึ่งเป็นรูปแบบของผลิตภัณฑ์คาร์ทีเซียนและมาพร้อมกับคำสั่งโดยนัย) หรือพวกเขาสามารถตั้งชื่อคุณสมบัติของชั้นเรียน (ไม่มีคำสั่งเบื้องต้น) หรือค่าพจนานุกรมหรือพวกเขาสามารถเป็นไฟล์ ไดเรกทอรีหรืออะไรก็ตาม เมื่อใดก็ตามที่คุณสามารถระบุ (iter หรือ getitem) บางอย่างใน 'container' หรือ 'context' คุณจะเห็น 'container' นั้นเป็นเวกเตอร์และกำหนด ops แบบไบนารี en.wikipedia.org/wiki/…
Niriel

ไม่มีค่าอะไรที่inไม่ควรใช้กับรายการเพราะเป็นการสแกนเชิงเส้นขององค์ประกอบและทำการเปรียบเทียบช้า ใช้ชุดอุปกรณ์แทนโดยเฉพาะถ้าการทดสอบความเป็นสมาชิกนั้นจะทำซ้ำ ๆ
cs95

22

y.count()คุณสามารถใช้

มันจะคืนค่าจำนวนเต็มของจำนวนครั้งที่สตริงย่อยปรากฏในสตริง

ตัวอย่างเช่น:

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

8
นับสตริงเป็นค่าใช้จ่ายเมื่อคุณเพียงต้องการที่จะตรวจสอบถ้ามันมี ...
Jean-Françoisฟาเบร

3
วิธีการที่มีอยู่ในโพสต์ต้นฉบับจากปี 2010 ดังนั้นฉันจึงลงเอยด้วยการแก้ไขโดยมีฉันทามติจากชุมชน (ดู meta post meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre

17
ไม่ ประเด็นของฉันคือ "ทำไมต้องตอบคำถามแบบเดียวกันกับที่คนอื่นทำเมื่อ 9 ปีก่อน"?
Jean-François Fabre

10
เพราะฉันเป็นผู้ดูแลเว็บไซต์ ... ฉันถามคำถามเกี่ยวกับเมตาmeta.stackoverflow.com/questions/385063/ …
Jean-François Fabre

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', ' ', ' ']
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.