Buscando una lista de objetos en Python

94

Supongamos que estoy creando una clase simple para que funcione de manera similar a una estructura de estilo C, solo para contener elementos de datos. Estoy tratando de averiguar cómo buscar en una lista de objetos objetos con un atributo que iguale un cierto valor. A continuación se muestra un ejemplo trivial para ilustrar lo que estoy tratando de hacer.

Por ejemplo:

class Data:
    pass

myList = []

for i in range(20):
    data = Data()
    data.n = i
    data.n_squared = i * i
    myList.append(data)

¿Cómo buscaría en la lista myList para determinar si contiene un elemento con n == 5?

He estado buscando en Google y buscando los documentos de Python, y creo que podría hacer esto con una lista de comprensión, pero no estoy seguro. Debo agregar que, por cierto, tengo que usar Python 2.4.3, por lo que las nuevas funciones gee-whiz 2.6 o 3.x no están disponibles para mí.

m0j0
fuente
Quizás una peculiaridad no intencional de su ejemplo: myList = [Data (). N == 0, Data (). N = 1, ...] donde data.n sería asignado por range () y data.n sería el indexar en myList. Por lo tanto, le permite extraer cualquier instancia de Data () simplemente haciendo referencia a myList por un valor de índice. Por supuesto, más tarde podría modificar myList [0] .n = 5.2 o algo así. Y el ejemplo quizás se simplificó demasiado.
DevPlayer

Respuestas:

139

Puede obtener una lista de todos los elementos coincidentes con una lista de comprensión:

[x for x in myList if x.n == 30]  # list of all elements with .n==30

Si simplemente desea determinar si la lista contiene algún elemento que coincida y hacerlo (relativamente) eficientemente, puede hacerlo

def contains(list, filter):
    for x in list:
        if filter(x):
            return True
    return False

if contains(myList, lambda x: x.n == 3)  # True if any element has .n==3
    # do stuff
Adam Rosenfield
fuente
25
o cualquier (filtro_personalizado (x) para x en myList si xn == 30) que es solo su función "contiene" como una función incorporada.
nosklo
Error de sintaxis en nosklo: se necesita un conjunto adicional de () alrededor del generador.
Gahooa
No tan. Pruébalo y verás.
Robert Rossney
1
Sería bueno fusionar esta respuesta con la de gahooa ( stackoverflow.com/a/598602/2349267 ).
Roman Hwang
77

Simple, elegante y potente:

Una expresión generadora en conjunción con un incorporado… (python 2.5+)

any(x for x in mylist if x.n == 10)

Utiliza la función any()incorporada de Python , que se define de la siguiente manera:

any (iterable) -> Devuelve True si algún elemento del iterable es verdadero. Equivalente a:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
gahooa
fuente
Agradable. Para su información, puede hacer cualquier (x para x en mylist si xn == 10) para guardar algunos parens (también == not =).
Jacob Gabrielson
Prefiero el uso, any(x for x in mylist if x['n'] == 10)pero es una buena idea
Alex Montoya
48

Solo para completar, no olvidemos la cosa más simple que posiblemente podría funcionar:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"
Charlie martin
fuente
39
[x for x in myList if x.n == 30]               # list of all matches
[x.n_squared for x in myList if x.n == 30]     # property of matches
any(x.n == 30 for x in myList)                 # if there is any matches
[i for i,x in enumerate(myList) if x.n == 30]  # indices of all matches

def first(iterable, default=None):
  for item in iterable:
    return item
  return default

first(x for x in myList if x.n == 30)          # the first match, if any
Markus Jarderot
fuente
1
Esta es una buena respuesta debido al "primer" método, que probablemente sea el caso de uso más común.
galarant
¡muchas gracias! los índices de coincidencia eran lo que estaba buscando. ¿Existe un atajo para usar esto para indexar directamente la lista para acceder a otro campo? Ahora obtengo una lista de entradas de lista (solo hay una entrada, por lo que es una lista con un elemento). Para obtener el índice, necesito realizar el resultado [0] antes de poder usarlo para indexar la lista. Desde el ejemplo de la pregunta, quiero acceder a n_squared desde un n particular: myList [índice de myList.n == 5] .n_squared
Frieke
32
filter(lambda x: x.n == 5, myList)
vartec
fuente
25
para alguien que quiera aprender Python, comprender lambda es básico.
vartec
2
Bueno, sí y no, con listas comprensivas y ordenadores de funciones clave como operator.attrgetter, casi nunca uso lambdas.
Ben Hoyt
9

Puede usar inpara buscar un elemento en una colección y una lista de comprensión para extraer el campo que le interesa. Esto (funciona para listas, conjuntos, tuplas y cualquier cosa que defina__contains__ o __getitem__).

if 5 in [data.n for data in myList]:
    print "Found it"

Ver también:

Tom Dunham
fuente
4

Debe agregar un método __eq__y un __hash__a su Dataclase, podría verificar si el__dict__ atributos son iguales (las mismas propiedades) y luego si sus valores también son iguales.

Si hiciste eso, puedes usar

test = Data()
test.n = 5

found = test in myList

La inpalabra clave comprueba si testestá enmyList .

Si solo desea una npropiedad Data, puede usar:

class Data(object):
    __slots__ = ['n']
    def __init__(self, n):
        self.n = n
    def __eq__(self, other):
        if not isinstance(other, Data):
            return False
        if self.n != other.n:
            return False
        return True
    def __hash__(self):
        return self.n

    myList = [ Data(1), Data(2), Data(3) ]
    Data(2) in myList  #==> True
    Data(5) in myList  #==> False
Johannes Weiss
fuente
3

Considere usar un diccionario:

myDict = {}

for i in range(20):
    myDict[i] = i * i

print(5 in myDict)
dan-gph
fuente
O: d = dict ((i, i * i) para i en el rango (20))
hughdbrown
Resuelve el problema trivial que usé para ilustrar mi pregunta, pero realmente no resolvió mi pregunta raíz. La respuesta que estaba buscando (hace más de 5 años) era la comprensión de la lista. :)
m0j0
1

Otra forma de hacerlo es usando la función next ().

matched_obj = next(x for x in list if x.n == 10)
Oliver Breeden
fuente