Python Language
Sökande
Sök…
Anmärkningar
Alla sökande algoritmer på iterables som innehåller n
element har O(n)
komplexitet. Endast specialiserade algoritmer som bisect.bisect_left()
kan vara snabbare med O(log(n))
komplexitet.
Få index för strängar: str.index (), str.rindex () och str.find (), str.rfind ()
String
har också en index
men också mer avancerade alternativ och den extra str.find
. För båda dessa finns det en kompletterande omvänd metod.
astring = 'Hello on StackOverflow'
astring.index('o') # 4
astring.rindex('o') # 20
astring.find('o') # 4
astring.rfind('o') # 20
Skillnaden mellan index
/ rindex
och find
/ rfind
är vad som händer om substrängen inte hittas i strängen:
astring.index('q') # ValueError: substring not found
astring.find('q') # -1
Alla dessa metoder tillåter ett start- och slutindex:
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: substring hittades inte
astring.rindex('o', 20) # 20
astring.rindex('o', 19) # 20 - still from left to right
astring.rindex('o', 4, 7) # 6
Söker efter ett element
Alla inbyggda samlingar i Python implementerar ett sätt att kontrollera elementmedlemskap med in
.
Lista
alist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 in alist # True
10 in alist # False
tupel
atuple = ('0', '1', '2', '3', '4')
4 in atuple # False
'4' in atuple # True
Sträng
astring = 'i am a string'
'a' in astring # True
'am' in astring # True
'I' in astring # False
Uppsättning
aset = {(10, 10), (20, 20), (30, 30)}
(10, 10) in aset # True
10 in aset # False
dict
dict
är lite speciell: det normala in
endast kontroller nycklarna. Om du vill söka i värden måste du ange det. Samma om du vill söka efter nyckelvärdespar .
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
Hämta indexlistan och tuples: list.index (), tuple.index ()
list
och tuple
har ett index
-metoden för att få positionen för elementet:
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 finns inte i listan
Men returnerar bara positionen för det först hittade elementet:
atuple = (10, 16, 26, 5, 2, 19, 105, 26)
atuple.index(26) # 2
atuple[2] # 26
atuple[7] # 26 - is also 26!
Söker nyckel (er) efter ett värde i dict
dict
har ingen inbyggd metod för att söka efter ett värde eller nyckel eftersom ordböcker är oordnade. Du kan skapa en funktion som får nyckeln (eller nycklarna) för ett angivet värde:
def getKeysForValue(dictionary, value):
foundkeys = []
for keys in dictionary:
if dictionary[key] == value:
foundkeys.append(key)
return foundkeys
Detta kan också skrivas som en motsvarande listaförståelse:
def getKeysForValueComp(dictionary, value):
return [key for key in dictionary if dictionary[key] == value]
Om du bara bryr dig om en nyckel som hittades:
def getOneKeyForValue(dictionary, value):
return next(key for key in dictionary if dictionary[key] == value)
De två första funktionerna returnerar en list
med alla keys
som har det angivna värdet:
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) # []
Den andra returnerar bara en nyckel:
getOneKeyForValue(adict, 10) # 'c' - depending on the circumstances this could also be 'a'
getOneKeyForValue(adict, 20) # 'b'
och höja en StopIteration
- Exception
om värdet inte finns i dict
:
getOneKeyForValue(adict, 25)
StopIteration
Hämta indexet för sorterade sekvenser: bisect.bisect_left ()
Sorterade sekvenser tillåter användning av snabbare bisect.bisect_left()
: 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
För mycket stora sorterade sekvenser kan hastighetsförstärkningen vara ganska hög. För den första sökningen ungefär 500 gånger så snabbt:
%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
Även om det är lite långsammare om elementet är ett av de allra första:
%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
Söker kapslade sekvenser
Att söka i kapslade sekvenser som en list
med tuple
kräver ett tillvägagångssätt som att söka på tangenterna efter värden i dict
men behöver anpassade funktioner.
Indexet för den yttersta sekvensen om värdet hittades i sekvensen:
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
eller indexet för den yttre och den inre sekvensen:
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
I allmänhet ( inte alltid ) att använda next
och ett generatoruttryck med villkor för att hitta den första förekomsten av det sökta värdet är det mest effektiva tillvägagångssättet.
Söker i anpassade klasser: __contain__ och __iter__
För att tillåta användning av in
för anpassade klasser måste klassen antingen tillhandahålla den magiska metoden __contains__
eller, om inte, en __iter__
__-metod.
Anta att du har en klass som innehåller en list
med list
:
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))
Att använda medlemstestning är möjligt genom att använda 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__.
även efter att du har __contains__
metoden __contains__
:
del ListList.__contains__
5 in a # True
# Prints: Using __iter__.
Obs: Den looping in
(som i for i in a
) använder alltid __iter__
även om klass implementerar en __contains__
metod.