Buscar..


Observaciones

Todos los algoritmos de búsqueda en iterables que contienen n elementos tienen complejidad O(n) . Solo los algoritmos especializados como bisect.bisect_left() pueden ser más rápidos con complejidad O(log(n)) .

Obtención del índice para cadenas: str.index (), str.rindex () y str.find (), str.rfind ()

String también tiene un método de index , pero también opciones más avanzadas y la función str.find adicional. Para ambos hay un método inverso complementario.

astring = 'Hello on StackOverflow'
astring.index('o')  # 4
astring.rindex('o') # 20

astring.find('o')   # 4
astring.rfind('o')  # 20

La diferencia entre index / rindex y find / rfind es lo que sucede si la subcadena no se encuentra en la cadena:

astring.index('q') # ValueError: substring not found
astring.find('q')  # -1

Todos estos métodos permiten un índice de inicio y finalización:

astring.index('o', 5)    # 6
astring.index('o', 6)    # 6 - start is inclusive
astring.index('o', 5, 7) # 6
astring.index('o', 5, 6) #  - end is not inclusive

ValueError: subcadena no encontrada

astring.rindex('o', 20) # 20 
astring.rindex('o', 19) # 20 - still from left to right

astring.rindex('o', 4, 7) # 6

Buscando un elemento

Todas las colecciones integradas en Python implementan una forma de verificar la pertenencia al elemento usando in .

Lista

alist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 in alist   # True
10 in alist  # False

Tupla

atuple = ('0', '1', '2', '3', '4')
4 in atuple    # False
'4' in atuple  # True

Cuerda

astring = 'i am a string'
'a' in astring   # True
'am' in astring  # True
'I' in astring   # False

Conjunto

aset = {(10, 10), (20, 20), (30, 30)}
(10, 10) in aset  # True
10 in aset        # False

Dictado

dict es un poco especial: lo normal in sólo comprueba las teclas. Si desea buscar en valores necesita especificarlo. Lo mismo si quieres buscar pares clave-valor .

adict = {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
1 in adict                 # True   - implicitly searches in keys
'a' in adict               # False
2 in adict.keys()          # True   - explicitly searches in keys
'a' in adict.values()      # True   - explicitly searches in values
(0, 'a') in adict.items()  # True   - explicitly searches key/value pairs

Obtención de la lista de índice y las tuplas: list.index (), tuple.index ()

list y tuple tienen un método de index para obtener la posición del elemento:

alist = [10, 16, 26, 5, 2, 19, 105, 26]
# search for 16 in the list
alist.index(16) # 1
alist[1]        # 16

alist.index(15)

ValueError: 15 no está en la lista

Pero solo devuelve la posición del primer elemento encontrado:

atuple = (10, 16, 26, 5, 2, 19, 105, 26)
atuple.index(26)   # 2
atuple[2]          # 26
atuple[7]          # 26 - is also 26!

Buscando clave (s) para un valor en dict

dict no tiene un método incorporado para buscar un valor o clave porque los diccionarios no están ordenados. Puede crear una función que obtenga la clave (o claves) para un valor específico:

def getKeysForValue(dictionary, value):
    foundkeys = []
    for keys in dictionary:
        if dictionary[key] == value:
            foundkeys.append(key)
    return foundkeys

Esto también podría escribirse como una lista de comprensión equivalente:

def getKeysForValueComp(dictionary, value): 
    return [key for key in dictionary if dictionary[key] == value]

Si solo te importa una clave encontrada:

def getOneKeyForValue(dictionary, value):
    return next(key for key in dictionary if dictionary[key] == value)

Las primeras dos funciones devolverán una list de todas las keys que tienen el valor especificado:

adict = {'a': 10, 'b': 20, 'c': 10}
getKeysForValue(adict, 10)     # ['c', 'a'] - order is random could as well be ['a', 'c']
getKeysForValueComp(adict, 10) # ['c', 'a'] - dito
getKeysForValueComp(adict, 20) # ['b']
getKeysForValueComp(adict, 25) # []

El otro solo devolverá una clave:

getOneKeyForValue(adict, 10)   # 'c'  - depending on the circumstances this could also be 'a'
getOneKeyForValue(adict, 20)   # 'b'

y levante un StopIteration - Exception si el valor no está en el dict :

getOneKeyForValue(adict, 25)

StopIteration

Obtención del índice para secuencias ordenadas: bisect.bisect_left ()

Las secuencias ordenadas permiten el uso de algoritmos de búsqueda más rápidos: bisect.bisect_left() 1 :

import bisect

def index_sorted(sorted_seq, value):
    """Locate the leftmost value exactly equal to x or raise a ValueError"""
    i = bisect.bisect_left(sorted_seq, value)
    if i != len(sorted_seq) and sorted_seq[i] == value:
        return i
    raise ValueError

alist = [i for i in range(1, 100000, 3)] # Sorted list from 1 to 100000 with step 3
index_sorted(alist, 97285) # 32428
index_sorted(alist, 4)     # 1
index_sorted(alist, 97286)

ValueError

Para secuencias clasificadas muy grandes , la ganancia de velocidad puede ser bastante alta. En caso de que la primera búsqueda sea aproximadamente 500 veces más rápida:

%timeit index_sorted(alist, 97285)
# 100000 loops, best of 3: 3 µs per loop
%timeit alist.index(97285)
# 1000 loops, best of 3: 1.58 ms per loop

Si bien es un poco más lento si el elemento es uno de los primeros:

%timeit index_sorted(alist, 4)
# 100000 loops, best of 3: 2.98 µs per loop
%timeit alist.index(4)
# 1000000 loops, best of 3: 580 ns per loop

Buscando secuencias anidadas

La búsqueda en secuencias anidadas como una list de tuple requiere un enfoque como la búsqueda de valores en dict pero necesita funciones personalizadas.

El índice de la secuencia más externa si el valor se encontró en la secuencia:

def outer_index(nested_sequence, value):
    return next(index for index, inner in enumerate(nested_sequence) 
                      for item in inner 
                      if item == value)

alist_of_tuples = [(4, 5, 6), (3, 1, 'a'), (7, 0, 4.3)]
outer_index(alist_of_tuples, 'a')  # 1
outer_index(alist_of_tuples, 4.3)  # 2

o el índice de la secuencia externa e interna:

def outer_inner_index(nested_sequence, value):
    return next((oindex, iindex) for oindex, inner in enumerate(nested_sequence) 
                                 for iindex, item in enumerate(inner) 
                                 if item == value)

outer_inner_index(alist_of_tuples, 'a') # (1, 2)
alist_of_tuples[1][2]  # 'a'

outer_inner_index(alist_of_tuples, 7)   # (2, 0)
alist_of_tuples[2][0]  # 7

En general ( no siempre ) el uso del next y una expresión generadora con condiciones para encontrar la primera aparición del valor buscado es el enfoque más eficiente.

Búsqueda en clases personalizadas: __contains__ y __iter__

Para permitir el uso de in para clases personalizadas, la clase debe proporcionar el método mágico __contains__ o, en su defecto, un método __iter__ .

Supongamos que tiene una clase que contiene una list de list s:

class ListList:
    def __init__(self, value):
        self.value = value
        # Create a set of all values for fast access
        self.setofvalues = set(item for sublist in self.value for item in sublist)
        
    def __iter__(self):
        print('Using __iter__.')
        # A generator over all sublist elements
        return (item for sublist in self.value for item in sublist)
        
    def __contains__(self, value):
        print('Using __contains__.')
        # Just lookup if the value is in the set
        return value in self.setofvalues

        # Even without the set you could use the iter method for the contains-check:
        # return any(item == value for item in iter(self))

Usar la prueba de membresía es posible usando in :

a = ListList([[1,1,1],[0,1,1],[1,5,1]])
10 in a    # False
# Prints: Using __contains__.
5 in a     # True
# Prints: Using __contains__.

Incluso después de eliminar el método __contains__ :

del ListList.__contains__
5 in a     # True
# Prints: Using __iter__.

Nota: El bucle in (como en for i in a ) siempre usará __iter__ incluso si la clase implementa un método __contains__ .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow