Recherche…


Remarques

Tous les algorithmes de recherche sur les itérables contenant n éléments ont une complexité O(n) . Seuls les algorithmes spécialisés comme bisect.bisect_left() peuvent être plus rapides avec la complexité O(log(n)) .

Obtenir l'index des chaînes: str.index (), str.rindex () et str.find (), str.rfind ()

String également une méthode d' index mais aussi des options plus avancées et le str.find supplémentaire. Pour les deux, il existe une méthode inversée complémentaire.

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

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

La différence entre index / rindex et find / rfind est ce qui arrive si la sous-chaîne n'est pas trouvée dans la chaîne:

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

Toutes ces méthodes permettent un index de début et de fin:

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: sous-chaîne introuvable

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

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

Recherche d'un élément

Toutes les collections intégrées dans Python implémentent un moyen de vérifier l'appartenance à un élément en utilisant in .

liste

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

Tuple

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

Chaîne

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

Ensemble

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

Dict

dict est un peu spécial: la normale in vérifie que les clés. Si vous souhaitez rechercher des valeurs, vous devez le spécifier. La même chose si vous souhaitez rechercher des paires clé-valeur .

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

Obtenir la liste d'index et les tuples: list.index (), tuple.index ()

list et tuple ont une méthode d' index pour obtenir la position de l'élément:

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 n'est pas dans la liste

Mais ne retourne que la position du premier élément trouvé:

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

Recherche de clé (s) pour une valeur dans dict

dict n'ont pas de méthode intégrée pour rechercher une valeur ou une clé car les dictionnaires ne sont pas ordonnés. Vous pouvez créer une fonction qui obtient la clé (ou les clés) pour une valeur spécifiée:

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

Cela pourrait également être écrit comme une liste de compréhension équivalente:

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

Si vous ne vous souciez que d'une clé trouvée:

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

Les deux premières fonctions renverront une list de toutes les keys ayant la valeur spécifiée:

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) # []

L'autre ne renverra qu'une clé:

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

et StopIteration une StopIteration - Exception si la valeur n'est pas dans le dict :

getOneKeyForValue(adict, 25)

StopIteration

Obtenir l'index des séquences triées: bisect.bisect_left ()

Les séquences triées permettent l'utilisation d'algorithmes de recherche plus rapides: 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)

ValeurErreur

Pour les très grandes séquences triées, le gain de vitesse peut être assez élevé. Pour la première recherche, environ 500 fois plus rapidement:

%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

Bien que ce soit un peu plus lent si l'élément est l'un des tous premiers:

%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

Recherche de séquences imbriquées

La recherche dans des séquences imbriquées comme une list de tuple nécessite une approche telle que la recherche de clés dans les dict mais nécessite des fonctions personnalisées.

L'index de la séquence la plus externe si la valeur a été trouvée dans la séquence:

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

ou l'index de la séquence externe et interne:

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 général ( pas toujours ) utiliser next et une expression de générateur avec des conditions pour trouver la première occurrence de la valeur recherchée est l'approche la plus efficace.

Recherche dans des classes personnalisées: __contains__ et __iter__

Pour autoriser l'utilisation de in pour les classes personnalisées, la classe doit fournir la méthode magique __contains__ ou, à défaut, une __iter__ __iter__.

Supposons que vous ayez une classe contenant une 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))

L'utilisation du test d'appartenance est possible en utilisant 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__.

même après la suppression de la méthode __contains__ :

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

Remarque: La boucle in (comme for i in a ) utilisera toujours __iter__ même si la classe implémente une __contains__ méthode.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow