Điều này thực hiện những gì bạn muốn và sẽ hoạt động trong hầu hết các trường hợp:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
Biểu thức 'a','b' in ['b', 'a', 'foo', 'bar']không hoạt động như mong đợi vì Python diễn giải nó như một bộ tuple:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Sự lựa chọn khác
Có nhiều cách khác để thực hiện kiểm tra này, nhưng chúng sẽ không hoạt động với nhiều loại đầu vào khác nhau. Như Kabie đã chỉ ra, bạn có thể giải quyết vấn đề này bằng cách sử dụng bộ ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...đôi khi:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Bộ chỉ có thể được tạo bằng các phần tử có thể băm. Nhưng biểu thức trình tạo all(x in container for x in items)có thể xử lý hầu hết mọi loại vùng chứa. Yêu cầu duy nhất là có containerthể lặp lại (nghĩa là không phải là trình tạo). itemscó thể được lặp lại bất kỳ.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Kiểm tra tốc độ
Trong nhiều trường hợp, kiểm tra tập hợp con sẽ nhanh hơn all, nhưng sự khác biệt không gây sốc - ngoại trừ khi câu hỏi không liên quan vì tập hợp không phải là một lựa chọn. Việc chuyển đổi danh sách thành các tập hợp chỉ cho mục đích kiểm tra như thế này sẽ không phải lúc nào cũng đáng gặp rắc rối. Và việc chuyển đổi bộ tạo thành bộ đôi khi có thể vô cùng lãng phí, làm chậm các chương trình theo nhiều mức độ lớn.
Dưới đây là một vài tiêu chuẩn để minh họa. Sự khác biệt lớn nhất đến khi cả hai containervà itemstương đối nhỏ. Trong trường hợp đó, cách tiếp cận tập hợp con nhanh hơn về thứ tự cường độ:
>>> smallset = set(range(10))
>>> smallsubset = set(range(5))
>>> %timeit smallset >= smallsubset
110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit all(x in smallset for x in smallsubset)
951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Điều này trông giống như một sự khác biệt lớn. Nhưng miễn containerlà một bộ, allvẫn hoàn toàn có thể sử dụng được ở quy mô lớn hơn rất nhiều:
>>> bigset = set(range(100000))
>>> bigsubset = set(range(50000))
>>> %timeit bigset >= bigsubset
1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit all(x in bigset for x in bigsubset)
5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Sử dụng thử nghiệm tập hợp con vẫn nhanh hơn, nhưng chỉ khoảng 5x ở quy mô này. Việc tăng tốc độ là do ctriển khai dự phòng nhanh của Python set, nhưng thuật toán cơ bản là giống nhau trong cả hai trường hợp.
Nếu của bạn itemsđã được lưu trữ trong danh sách vì những lý do khác, thì bạn sẽ phải chuyển chúng thành một tập hợp trước khi sử dụng phương pháp kiểm tra tập hợp con. Sau đó, tốc độ giảm xuống khoảng 2,5 lần:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Và nếu của bạn containerlà một chuỗi và cần được chuyển đổi trước, thì tốc độ tăng thậm chí còn nhỏ hơn:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Lần duy nhất chúng ta nhận được kết quả chậm thảm hại là khi chúng ta rời đi containernhư một chuỗi:
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Và tất nhiên, chúng tôi sẽ chỉ làm điều đó nếu chúng tôi phải làm. Nếu tất cả các mục trong bigseqđều có thể băm, thì chúng tôi sẽ thực hiện việc này thay thế:
>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq)
7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Nhanh hơn chỉ 1,66 lần so với giải pháp thay thế ( set(bigseq) >= set(bigsubseq), được tính thời gian ở trên là 4,36).
Vì vậy, kiểm tra tập hợp con thường nhanh hơn, nhưng không phải bởi một lợi nhuận đáng kinh ngạc. Mặt khác, hãy xem khi nào allthì nhanh hơn. Điều gì sẽ xảy ra nếu itemsgiá trị mười triệu dài và có khả năng có các giá trị không bằng container?
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter)
13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter)
2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Chuyển đổi máy phát điện thành một bộ hóa ra là vô cùng lãng phí trong trường hợp này. Hàm settạo phải sử dụng toàn bộ bộ tạo. Nhưng hành vi đoản mạch của allđảm bảo rằng chỉ một phần nhỏ của máy phát điện cần được tiêu thụ, vì vậy nó nhanh hơn thử nghiệm tập hợp con bốn bậc độ lớn .
Đây là một ví dụ cực đoan, phải thừa nhận. Nhưng như nó cho thấy, bạn không thể cho rằng cách tiếp cận này hay cách khác sẽ nhanh hơn trong mọi trường hợp.
Ảnh chụp ngược
Hầu hết thời gian, chuyển đổi containerthành một tập hợp là đáng giá, ít nhất là nếu tất cả các phần tử của nó đều có thể băm được. Đó là bởi vì inđối với tập hợp là O (1), trong khi inđối với trình tự là O (n).
Mặt khác, việc sử dụng thử nghiệm tập hợp con có lẽ đôi khi chỉ có giá trị. Chắc chắn làm điều đó nếu các mục thử nghiệm của bạn đã được lưu trữ trong một bộ. Nếu không, allchỉ chậm hơn một chút và không yêu cầu bất kỳ bộ nhớ bổ sung nào. Nó cũng có thể được sử dụng với các bộ tạo vật phẩm lớn, và đôi khi cung cấp một tốc độ lớn trong trường hợp đó.
set(['a', 'b']) <= set(['b','a','foo','bar'])là một cách khác để đánh vần cùng một thứ, và trông "mathier".