Ho un elenco di set:
setlist = [s1,s2,s3...]
Voglio s1 ∩ s2 ∩ s3 ...
Posso scrivere una funzione per farlo eseguendo una serie di coppie s1.intersection(s2)
, ecc.
Esiste un modo consigliato, migliore o integrato?
Ho un elenco di set:
setlist = [s1,s2,s3...]
Voglio s1 ∩ s2 ∩ s3 ...
Posso scrivere una funzione per farlo eseguendo una serie di coppie s1.intersection(s2)
, ecc.
Esiste un modo consigliato, migliore o integrato?
Risposte:
Da Python versione 2.6 in poi puoi usare più argomenti per set.intersection()
, come
u = set.intersection(s1, s2, s3)
Se i set sono in un elenco, questo si traduce in:
u = set.intersection(*setlist)
dove *a_list
è l'espansione della lista
Si noti che nonset.intersection
è un metodo statico, ma utilizza la notazione funzionale per applicare l'intersezione del primo set con il resto dell'elenco. Quindi, se l'elenco degli argomenti è vuoto, ciò fallirà.
A partire da 2.6, set.intersection
prende arbitrariamente molti iterabili.
>>> 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])
Chiaramente set.intersection
è quello che vuoi qui, ma nel caso in cui tu abbia mai bisogno di una generalizzazione di "prendi la somma di tutti questi", "prendi il prodotto di tutti questi", "prendi il xor di tutti questi", quello che stai cercando è il reduce
funzione:
from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
o
print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
Se non hai Python 2.6 o versioni successive, l'alternativa è scrivere un esplicito per loop:
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])
Puoi anche usare reduce
:
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])
Tuttavia, a molti programmatori di Python non piace, incluso lo stesso Guido :
Circa 12 anni fa, Python ha acquisito lambda, reduce (), filter () e map (), per gentile concessione di (credo) un hacker Lisp che li ha persi e ha inviato patch funzionanti. Ma, nonostante il valore di PR, penso che queste funzionalità dovrebbero essere tagliate da Python 3000.
Quindi ora riduci (). Questo è in realtà quello che ho sempre odiato di più, perché, a parte alcuni esempi che coinvolgono + o *, quasi ogni volta che vedo una chiamata ridurre () con un argomento di funzione non banale, devo prendere carta e penna per diagramma cosa viene effettivamente immesso in quella funzione prima di capire cosa dovrebbe fare il riduttore (). Quindi, nella mia mente, l'applicabilità di reduce () è praticamente limitata agli operatori associativi, e in tutti gli altri casi è meglio scrivere esplicitamente il ciclo di accumulazione.
result
è vuoto.
Qui sto offrendo una funzione generica per intersezioni di più insiemi cercando di sfruttare il miglior metodo disponibile:
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:])
A Guido potrebbe non piacere reduce
, ma mi piace un po ':)
sets
invece di provare ad accedere sets[0]
e catturare il file IndexError
.
a_set
viene utilizzato al ritorno finale.
return reduce(sets[0], sets[1:]) if sets else set()
?
try
/ except
dovrebbe essere evitato, se possibile. È un odore di codice, è inefficiente e può nascondere altri problemi.
reduce
è "limitato agli operatori associativi", che è applicabile in questo caso.reduce
è molto spesso difficile da capire, ma&
non è poi così male.