У мене є список наборів:
setlist = [s1,s2,s3...]
Я хочу s1 ∩ s2 ∩ s3 ...
Я можу написати функцію, щоб це виконувати, виконуючи серію попарно s1.intersection(s2)
тощо.
Чи є рекомендований, кращий або вбудований спосіб?
У мене є список наборів:
setlist = [s1,s2,s3...]
Я хочу s1 ∩ s2 ∩ s3 ...
Я можу написати функцію, щоб це виконувати, виконуючи серію попарно s1.intersection(s2)
тощо.
Чи є рекомендований, кращий або вбудований спосіб?
Відповіді:
З Python версії 2.6 ви можете використовувати декілька аргументів на set.intersection()
, наприклад
u = set.intersection(s1, s2, s3)
Якщо набори є у списку, це означає:
u = set.intersection(*setlist)
де *a_list
це розширення списку
Зауважте, що set.intersection
це не статичний метод, але для використання перетину першого набору з рештою списку використовується функціональна позначення. Отже, якщо список аргументів порожній, це не вдасться.
Станом на 2.6, set.intersection
бере довільно багато ітерабелів.
>>> 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])
Зрозуміло set.intersection
, що ви хочете тут, але у випадку, коли вам колись потрібне узагальнення "взяти суму всіх цих", "візьміть добуток усіх цих", "візьміть за собою все це", те, що ви шукаєте, це reduce
функція:
from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
або
print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
Якщо у вас немає Python 2.6 або новішої версії, альтернативою є написання явного циклу:
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])
Ви також можете використовувати reduce
:
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])
Однак багатьом програмістам Python це не подобається, в тому числі сам Гвідо :
Близько 12 років тому Python отримав лямбда, зменшити (), фільтрувати () та карту (), люб’язно (я вважаю) хакера Lisp, який пропустив їх та подав робочі патчі. Але, незважаючи на цінність PR, я думаю, що ці функції слід вирішити з Python 3000.
Тому тепер зменшіть (). Це насправді той, якого я завжди найбільше ненавиджу, тому що, окрім кількох прикладів, що включають + або *, майже кожного разу, коли я бачу виклик зменшення () з аргументом нетривіальної функції, мені потрібно захопити ручку та папір для Діаграма того, що насправді подається у цю функцію, перш ніж я зрозумію, що слід робити (). Отже, на мій погляд, застосуваність redu () значною мірою обмежена асоціативними операторами, і в усіх інших випадках краще виписати цикл накопичення чітко.
result
порожній.
Тут я пропоную загальну функцію для множинного перетину, намагаючись скористатися найкращим доступним методом:
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:])
Гвідо може не подобатися reduce
, але мені це подобається :)
sets
замість того, щоб намагатися отримати доступ sets[0]
та ловити IndexError
.
a_set
використовується при остаточному поверненні.
return reduce(sets[0], sets[1:]) if sets else set()
?
try
/, except
якщо можна. Це кодовий запах, неефективний і може приховати інші проблеми.
reduce
"обмежується асоціативними операторами", що застосовується в цьому випадку.reduce
Це дуже часто важко зрозуміти, але&
це не так вже й погано.