actualización ::: publicación contiene una referencia a acusaciones falsas de rendimiento inferior de conjuntos en comparación con conjuntos congelados. Sigo diciendo que todavía es sensato usar un juego frozenset en este caso, incluso si no hay necesidad de ajustar el conjunto en sí mismo, simplemente porque es más correcto semánticamente. Aunque, en la práctica, es posible que no me moleste en escribir los 6 caracteres adicionales. No me siento motivado para revisar y editar la publicación, así que tenga en cuenta que el vínculo de "acusaciones" se vincula con algunas pruebas ejecutadas incorrectamente. Los detalles sangrientos se resumen en los comentarios. ::: actualización
El segundo trozo de código posted por Brandon Craig Rodas es bastante bueno, pero como él no respondió a mi sugerencia sobre el uso de un frozenset (bueno, no cuando empecé a escribir esto, de todos modos) , Voy a seguir adelante y publicarlo yo mismo.
Toda la base de la empresa en cuestión es comprobar si cada una de una serie de valores (L1
) están en otro conjunto de valores; ese conjunto de valores es el contenido de L2
y L3
. El uso de la palabra "ajuste" en esa frase está diciendo: a pesar de que L2
y L3
son list
s, que realmente no se preocupan por sus propiedades de lista como, como el orden en que sus valores están en o cuántos de cada uno se Contiene. Nos importa el conjunto (existe otra vez) de los valores que colectivamente contienen.
Si ese conjunto de valores se almacena como una lista, usted tiene que pasar por la lista de elementos, uno por uno, comprobando cada uno. Es relativamente lento, y es una semántica mala: una vez más, es un "conjunto" de valores, no una lista. Por lo tanto, Python tiene estos tipos de conjunto ordenado que contienen un conjunto de valores únicos y puede indicarle rápidamente si hay algún valor en ellos o no. Esto funciona de la misma manera que los tipos dict
de python funcionan cuando buscas una clave.
La diferencia entre los conjuntos y frozensets es que los conjuntos son mutables, es decir que pueden ser modificados después de la creación. La documentación en ambos tipos es here.
Dado que el conjunto que necesitamos crear, la unión de los valores almacenados en L2
y L3
, no se va a modificar una vez creado, es semánticamente apropiado utilizar un tipo de datos inmutables. Esto también tiene algunos beneficios de rendimiento. Bueno, tiene sentido que tenga alguna ventaja; de lo contrario, ¿por qué Python tendría frozenset
como un builtin?
actualización ...
Brandon ha respondido a esta pregunta: ¿la verdadera ventaja de los conjuntos de congelados es que su inmutabilidad hace que sea posible para que sean hashable, que les permite ser claves de diccionario o miembros de otros conjuntos .
Realicé algunas pruebas de temporización informales que comparaban la velocidad de creación y búsqueda en conjuntos relativamente grandes (3000 elementos) congelados y cambiables; no hubo mucha diferencia. Esto entra en conflicto con el enlace anterior, pero respalda lo que dice Brandon sobre que son idénticos, pero por el aspecto de la mutabilidad.
... actualización
Ahora, debido frozensets son inmutables, que no tienen un método de actualización. Brandon usó el método set.update
para evitar crear y luego descartar una lista temporal en el camino para establecer la creación; Voy a tomar un enfoque diferente.
items = (item for lst in (L2, L3) for item in lst)
Esto hace generator expressionitems
un iterador sobre, consecutivamente, el contenido de L2
y L3
. No solo eso, sino que lo hace sin crear una lista completa, llena de objetos intermedios. Usar expresiones anidadas for
en generadores es un poco confuso, pero logro mantenerlo ordenado al recordar que anidan en el mismo orden en que lo harían si escribiera bucles for reales, p.
def get_items(lists):
for lst in lists:
for item in lst:
yield item
Eso generator function es equivalente a la expresión generador que asignamos a items
. Bueno, excepto que es una definición de función parametrizada en lugar de una asignación directa a una variable.
De todos modos, suficiente digresión. El gran problema con los generadores es que en realidad no hacen nada. Bueno, al menos no de inmediato: simplemente configuran el trabajo que se realizará más tarde, cuando la expresión del generador es iterado. Esto se conoce formalmente como perezosa. Vamos a hacer eso (bueno, lo estoy, de todos modos) al pasar items
a la función frozenset
, que itera sobre él y devuelve un frozenset frio y frío.
unwanted = frozenset(items)
En realidad se podría combinar las dos últimas líneas, poniendo la expresión generador de la derecha dentro de la llamada a frozenset
:
unwanted = frozenset(item for lst in (L2, L3) for item in lst)
Este truco sintáctica ordenada funciona siempre y cuando el iterator creado por la expresión generador es el único parámetro para la función que está llamando. De lo contrario, tiene que escribirlo en su conjunto de paréntesis habitual, como si estuviera pasando una tupla como argumento para la función.
Ahora podemos construir una nueva lista de la misma manera que Brandon, con un list comprehension. Estos utilizan la misma sintaxis que las expresiones de generador, y básicamente hacen lo mismo, excepto que son con ganas en lugar de (de nuevo, estos son términos técnicos reales), por lo que se ponen a trabajar iterando sobre los elementos y creando una lista de ellos.
L4 = [item for item in L1 if item not in unwanted]
Esto es equivalente a pasar una expresión generador para list
, por ejemplo
L4 = list(item for item in L1 if item not in unwanted)
pero más idiomático.
Así que esto va a crear la lista L4
, que contiene los elementos de L1
que no estaban en cualquiera L2
o L3
, manteniendo el orden en que fueron originalmente en el número de ellos que había.
Si lo que desea saber cuales valores están en L1
pero no en L2
o L3
, es mucho más fácil: sólo tiene que crear ese conjunto:
L1_unique_values = set(L1) - unwanted
Usted puede hacer una lista cabo de él, as does st0le, pero eso podría no ser realmente lo que quieres. Si realmente desea que el establecer de valores que sólo se encuentran en L1
, puede que tenga una muy buena razón para mantener esa conjunto como set
, o incluso un frozenset
:
L1_unique_values = frozenset(L1) - unwanted
... Annnnd, ahora algo completamente diferente:
from itertools import ifilterfalse, chain
L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))
No hay una forma correcta de hacerlo hasta que decida si le importa o si no le importan los duplicados y el pedido. Probablemente algún tipo de lista de comprensión o de trabajo en función de lo que te interesa. – istruble
Además, ¿está bien suponer que todos los elementos en las listas serán manejables todo el tiempo? Si no, o a veces no, eso sería muy significativo. – martineau
¿Por qué no usa conjuntos para empezar? Entonces tu "aritmética" funcionaría. – poke