Python có phương thức chuỗi con 'chứa' chuỗi không?


3599

Tôi đang tìm kiếm một string.containshoặc string.indexofphương thức trong Python.

Tôi muốn làm:

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

Câu trả lời:


6263

Bạn có thể sử dụng intoán tử :

if "blah" not in somestring: 
    continue

231
Dưới mui xe, Python sẽ sử dụng __contains__(self, item), __iter__(self)__getitem__(self, key)theo thứ tự để xác định xem một mục nằm trong một định chứa. Thực hiện ít nhất một trong những phương pháp đó để incung cấp cho loại tùy chỉnh của bạn.
BallpointBen

27
Chỉ cần chắc chắn rằng nội địa sẽ không là Không có. Nếu không, bạn nhận được mộtTypeError: argument of type 'NoneType' is not iterable
Bí ngô lớn

5
FWIW, đây là cách thành ngữ để thực hiện mục tiêu đã nói.
Trenton

6
Đối với các chuỗi, intoán tử Python có sử dụng thuật toán Rabin-Carp không?
Sam trò chuyện

3
@SamChats xem stackoverflow.com/questions/18139460/ cho các chi tiết triển khai (trong CPython; afaik đặc tả ngôn ngữ không bắt buộc bất kỳ thuật toán cụ thể nào ở đây).
Christoph Burschka

667

Nếu đó chỉ là một tìm kiếm chuỗi con, bạn có thể sử dụng string.find("substring").

Bạn không cần phải là một chút cẩn thận với find, indexinmặc dù, khi chúng được substring tìm kiếm. Nói cách khác, điều này:

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

Nó sẽ in Found 'is' in the string.Tương tự, if "is" in s:sẽ đánh giá True. Điều này có thể hoặc không thể là những gì bạn muốn.


78
+1 để làm nổi bật các vấn đề liên quan đến tìm kiếm chuỗi con. giải pháp rõ ràng là if ' is ' in s:sẽ trở lại Falsenhư mong đợi (có thể).
aaronasterling

95
@aaronasterling Rõ ràng là có thể, nhưng không hoàn toàn chính xác. Điều gì nếu bạn có dấu câu hoặc nó ở đầu hoặc cuối? Viết hoa thì sao? Tốt hơn sẽ là một trường hợp tìm kiếm regex không nhạy cảm cho \bis\b(ranh giới từ).
Bob

2
@JamieBull Một lần nữa, bạn phải xem xét nếu bạn muốn bao gồm dấu chấm câu làm dấu phân cách cho một từ. Việc chia tách sẽ có tác dụng phần lớn giống như giải pháp ngây thơ của việc kiểm tra ' is ', đáng chú ý là nó sẽ không bắt được This is, a comma'hoặc 'It is.'.
Bob

7
@JamieBull: Tôi rất nghi ngờ bất kỳ sự phân chia đầu vào thực sự nào s.split(string.punctuation + string.whitespace)sẽ bị chia tách dù chỉ một lần; splitkhông giống như strip/ rstrip/ lstriphọ các hàm, nó chỉ phân tách khi nhìn thấy tất cả các ký tự phân cách, liên tục, theo thứ tự chính xác đó. Nếu bạn muốn phân chia trên các lớp ký tự, bạn quay lại các biểu thức thông thường (tại thời điểm đó, tìm kiếm r'\bis\b'mà không tách là cách đơn giản hơn, nhanh hơn để đi).
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, lấy điểm. Điều này bây giờ thật lố bịch ...
Jamie Bull

190

Python có một chuỗi chứa phương thức chuỗi con không?

Có, nhưng Python có một toán tử so sánh mà bạn nên sử dụng thay thế, bởi vì ngôn ngữ dự định sử dụng nó và các lập trình viên khác sẽ mong đợi bạn sử dụng nó. Từ khóa inđó, được sử dụng như một toán tử so sánh:

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

Ngược lại (bổ sung), mà câu hỏi ban đầu yêu cầu, là not in:

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

Điều này về mặt ngữ nghĩa giống như not 'foo' in '**foo**'nhưng nó dễ đọc hơn và được cung cấp rõ ràng bằng ngôn ngữ như một sự cải thiện khả năng đọc.

Tránh sử dụng __contains__, findindex

Như đã hứa, đây là containsphương pháp:

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

trả lại True. Bạn cũng có thể gọi hàm này từ thể hiện của siêu chuỗi:

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

Nhưng đừng. Các phương thức bắt đầu bằng dấu gạch dưới được coi là riêng tư về mặt ngữ nghĩa. Lý do duy nhất để sử dụng điều này là khi mở rộng innot inchức năng (ví dụ: nếu phân lớp 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')

và bây giờ:

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

Ngoài ra, tránh các phương thức chuỗi sau:

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

Các ngôn ngữ khác có thể không có phương pháp để kiểm tra trực tiếp các chuỗi con và vì vậy bạn sẽ phải sử dụng các loại phương thức này, nhưng với Python, sử dụng intoán tử so sánh sẽ hiệu quả hơn nhiều .

So sánh hiệu suất

Chúng ta có thể so sánh nhiều cách khác nhau để hoàn thành cùng một mục tiêu.

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

Và bây giờ chúng ta thấy rằng việc sử dụng innhanh hơn nhiều so với những người khác. Ít thời gian hơn để thực hiện một thao tác tương đương là tốt hơn:

>>> 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
Tại sao nên tránh str.indexstr.find? Làm thế nào khác bạn sẽ đề nghị ai đó tìm chỉ mục của một chuỗi con thay vì chỉ tồn tại hay không? (hoặc bạn có nghĩa là tránh sử dụng chúng ở vị trí chứa - vì vậy đừng sử dụng s.find(ss) != -1thay vì ss in s?)
coderforlife

3
Chính xác là như vậy, mặc dù ý định đằng sau việc sử dụng các phương pháp đó có thể được giải quyết tốt hơn bằng cách sử dụng remô-đun một cách tao nhã . Tôi chưa tìm thấy cách sử dụng cho str.index hoặc str.find mình trong bất kỳ mã nào tôi đã viết.
Aaron Hall

Vui lòng mở rộng câu trả lời của bạn để tư vấn chống lại việc sử dụng str.countlà tốt ( string.count(something) != 0). rùng mình
cs95

Làm thế nào để operatorphiên bản mô-đun thực hiện?
jpmc26

@ jpmc26 giống như in_trên - nhưng với một stackframe xung quanh nó, nên nó chậm hơn thế: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall

175

if needle in haystack:là cách sử dụng bình thường, như @Michael nói - nó phụ thuộc vào intoán tử, dễ đọc hơn và nhanh hơn so với một cuộc gọi phương thức.

Nếu bạn thực sự cần một phương thức thay vì một toán tử (ví dụ để thực hiện một số điều kỳ lạ key=đối với một loại rất đặc biệt ...?), Thì đó sẽ là 'haystack'.__contains__. Nhưng vì ví dụ của bạn là để sử dụng trong một if, tôi đoán bạn không thực sự có nghĩa là những gì bạn nói ;-). Đây không phải là hình thức tốt (cũng không thể đọc, cũng không hiệu quả) để sử dụng trực tiếp các phương pháp đặc biệt - thay vào đó, chúng được sử dụng thông qua các toán tử và nội trang được ủy quyền cho chúng.


55

in Danh sách và chuỗi Python

Dưới đây là một vài ví dụ hữu ích tự nói về inphương pháp:

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

Hãy cẩn thận. Danh sách là iterables và inphương thức hoạt động trên iterables, không chỉ chuỗi.


1
Danh sách lặp có thể được chuyển xung quanh để tìm bất kỳ danh sách nào trong một chuỗi không? Vd : ["bar", "foo", "foobar"] in "foof"?
CaffeinatedCoder

1
@CaffeinatedCoder, không, điều này đòi hỏi phải lặp lại lồng nhau. Hoàn thành tốt nhất bằng cách tham gia danh sách với các ống "|" .join (["bar", "foo", "foobar"]) và biên dịch một biểu thức chính xác ra khỏi nó, sau đó khớp với "
fagger

2
bất kỳ ([x trong "giả mạo" cho x trong ["bar", "foo", "foobar"]])
Izaak Weiss

1
@Izaakweiss Một lớp lót của bạn hoạt động, nhưng nó không dễ đọc lắm và nó lặp đi lặp lại lồng nhau. Tôi sẽ khuyên bạn không nên làm điều này
Firelynx

1
@ PiyushS.Wanare bạn có ý gì bởi sự phức tạp? "WTF / min" cao hơn rất nhiều với regex.
Firelynx

42

Nếu bạn hài lòng với "blah" in somestringnhưng muốn nó là một cuộc gọi hàm / phương thức, có lẽ bạn có thể làm điều này

import operator

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

Tất cả các toán tử trong Python có thể được tìm thấy ít nhiều trong mô đun toán tử bao gồm in.


40

Vì vậy, rõ ràng không có gì tương tự để so sánh vector-khôn ngoan. Một cách Python rõ ràng để làm như vậy sẽ là:

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
Đó là bởi vì có rất nhiều cách để tạo ra Sản phẩm từ các biến nguyên tử. Bạn có thể nhét chúng vào một tuple, một danh sách (là các dạng Sản phẩm của Cartesian và đi kèm theo một thứ tự ngụ ý) hoặc chúng có thể được đặt tên là các thuộc tính của một lớp (không có thứ tự ưu tiên) hoặc các giá trị từ điển, hoặc chúng có thể là các tệp trong một thư mục, hoặc bất cứ điều gì. Bất cứ khi nào bạn có thể xác định duy nhất (iter hoặc getitem) một cái gì đó trong 'container' hoặc 'bối cảnh', bạn có thể thấy 'container' đó là một loại vectơ và xác định ops nhị phân trên nó. vi.wikipedia.org/wiki/ từ
Niriel

Không có giá trị gì không innên được sử dụng với các danh sách vì nó quét tuyến tính các phần tử và bị so sánh chậm. Sử dụng một bộ thay thế, đặc biệt là nếu các bài kiểm tra thành viên phải được thực hiện nhiều lần.
cs95

22

Bạn có thể sử dụng y.count().

Nó sẽ trả về giá trị nguyên của số lần một chuỗi con xuất hiện trong một chuỗi.

Ví dụ:

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

8
đếm một chuỗi rất tốn kém khi bạn chỉ muốn kiểm tra xem nó có ở đó không ...
Jean-François Fabre

3
các phương thức tồn tại trong bài viết gốc từ năm 2010 vì vậy tôi đã kết thúc việc chỉnh sửa chúng, với sự đồng thuận từ cộng đồng (xem meta bài meta.stackoverflow.com/questions / 385063 /
Jean-François Fabre

17
Không. Quan điểm của tôi là "tại sao trả lời chính xác như những gì người khác đã làm 9 năm trước"?
Jean-François Fabre

10
bởi vì tôi đang kiểm duyệt trang web ... Tôi đã đặt câu hỏi trên meta meta.stackoverflow.com/questions/385063/NH
Jean-François Fabre

2
sau đó Nếu bạn có thẩm quyền để loại bỏ nó thì hãy loại bỏ nó, hãy làm những gì bạn phải làm và tiếp tục. IMO câu trả lời này thêm giá trị, được phản ánh bởi lượt bình chọn từ người dùng.
Brandon Bailey

20

Đây là câu trả lời của bạn:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Để kiểm tra nếu nó sai:

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

HOẶC LÀ:

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

8

Bạn có thể sử dụng các biểu thức thông thường để có được các lần xuất hiện:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.