Tôi có một danh sách các bộ:
setlist = [s1,s2,s3...]
Tôi muốn s1 ∩ s2 s3 ...
Tôi có thể viết một hàm để làm điều đó bằng cách thực hiện một loạt các cặp s1.intersection(s2)
, v.v.
Có một cách được đề nghị, tốt hơn, hoặc tích hợp?
Tôi có một danh sách các bộ:
setlist = [s1,s2,s3...]
Tôi muốn s1 ∩ s2 s3 ...
Tôi có thể viết một hàm để làm điều đó bằng cách thực hiện một loạt các cặp s1.intersection(s2)
, v.v.
Có một cách được đề nghị, tốt hơn, hoặc tích hợp?
Câu trả lời:
Từ Python phiên bản 2.6 trở đi, bạn có thể sử dụng nhiều đối số để set.intersection()
, như
u = set.intersection(s1, s2, s3)
Nếu các bộ nằm trong một danh sách, điều này có nghĩa là:
u = set.intersection(*setlist)
nơi *a_list
là Mở rộng danh mục
Lưu ý rằng set.intersection
là không một phương pháp tĩnh, nhưng điều này sử dụng các ký hiệu chức năng để áp dụng giao điểm của tập đầu tiên với phần còn lại của danh sách. Vì vậy, nếu danh sách đối số trống, điều này sẽ thất bại.
Kể từ 2.6, set.intersection
có nhiều lần lặp tùy ý.
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 4, 6])
>>> s1 & s2 & s3
set([2])
>>> s1.intersection(s2, s3)
set([2])
>>> sets = [s1, s2, s3]
>>> set.intersection(*sets)
set([2])
Rõ ràng set.intersection
là những gì bạn muốn ở đây, nhưng trong trường hợp bạn cần một sự khái quát hóa "lấy tổng của tất cả những thứ này", "lấy sản phẩm của tất cả những thứ này", "lấy xor của tất cả những thứ này", thứ bạn đang tìm kiếm là reduce
chức năng:
from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
hoặc là
print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
Nếu bạn không có Python 2.6 trở lên, cách khác là viết một vòng lặp rõ ràng cho vòng lặp:
def set_list_intersection(set_list):
if not set_list:
return set()
result = set_list[0]
for s in set_list[1:]:
result &= s
return result
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print set_list_intersection(set_list)
# Output: set([1])
Bạn cũng có thể sử dụng reduce
:
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])
Tuy nhiên, nhiều lập trình viên Python không thích nó, bao gồm cả chính Guido :
Khoảng 12 năm trước, Python ngậm lambda, giảm (), bộ lọc () và bản đồ (), nhờ (tôi tin) một hacker Lisp đã bỏ lỡ chúng và gửi các bản vá hoạt động. Nhưng, mặc dù có giá trị PR, tôi nghĩ các tính năng này nên được cắt từ Python 3000.
Vì vậy, bây giờ giảm (). Đây thực sự là điều tôi luôn ghét nhất, bởi vì, ngoài một vài ví dụ liên quan đến + hoặc *, hầu như mỗi khi tôi thấy một cuộc gọi rút gọn () với một đối số chức năng không tầm thường, tôi cần lấy bút và giấy để sơ đồ những gì thực sự được đưa vào hàm đó trước khi tôi hiểu việc giảm () phải làm là gì. Vì vậy, trong suy nghĩ của tôi, khả năng áp dụng của less () bị giới hạn khá nhiều đối với các toán tử liên kết và trong tất cả các trường hợp khác, tốt hơn hết là viết ra vòng lặp tích lũy một cách rõ ràng.
result
trống.
Ở đây tôi đang cung cấp một hàm chung cho giao lộ nhiều bộ đang cố gắng tận dụng phương thức tốt nhất hiện có:
def multiple_set_intersection(*sets):
"""Return multiple set intersection."""
try:
return set.intersection(*sets)
except TypeError: # this is Python < 2.6 or no arguments
pass
try: a_set= sets[0]
except IndexError: # no arguments
return set() # return empty set
return reduce(a_set.intersection, sets[1:])
Guido có thể không thích reduce
, nhưng tôi thích nó :)
sets
thay vì cố gắng truy cập sets[0]
và bắt IndexError
.
a_set
được sử dụng ở lợi nhuận cuối cùng.
return reduce(sets[0], sets[1:]) if sets else set()
?
try
/ except
nên tránh nếu bạn có thể. Đó là một mùi mã, không hiệu quả và có thể che giấu các vấn đề khác.
reduce
là "giới hạn đối với các nhà khai thác liên kết", áp dụng trong trường hợp này.reduce
thường rất khó để tìm ra, nhưng&
không quá tệ.