Python Language
Tri, minimum et maximum
Recherche…
Obtenir le minimum ou le maximum de plusieurs valeurs
min(7,2,1,5)
# Output: 1
max(7,2,1,5)
# Output: 7
Utiliser l'argument clé
Trouver le minimum / maximum d'une séquence de séquences est possible:
list_of_tuples = [(0, 10), (1, 15), (2, 8)]
min(list_of_tuples)
# Output: (0, 10)
mais si vous voulez trier par un élément spécifique dans chaque séquence, utilisez l'argument key
:
min(list_of_tuples, key=lambda x: x[0]) # Sorting by first element
# Output: (0, 10)
min(list_of_tuples, key=lambda x: x[1]) # Sorting by second element
# Output: (2, 8)
sorted(list_of_tuples, key=lambda x: x[0]) # Sorting by first element (increasing)
# Output: [(0, 10), (1, 15), (2, 8)]
sorted(list_of_tuples, key=lambda x: x[1]) # Sorting by first element
# Output: [(2, 8), (0, 10), (1, 15)]
import operator
# The operator module contains efficient alternatives to the lambda function
max(list_of_tuples, key=operator.itemgetter(0)) # Sorting by first element
# Output: (2, 8)
max(list_of_tuples, key=operator.itemgetter(1)) # Sorting by second element
# Output: (1, 15)
sorted(list_of_tuples, key=operator.itemgetter(0), reverse=True) # Reversed (decreasing)
# Output: [(2, 8), (1, 15), (0, 10)]
sorted(list_of_tuples, key=operator.itemgetter(1), reverse=True) # Reversed(decreasing)
# Output: [(1, 15), (0, 10), (2, 8)]
Argument par défaut à max, min
Vous ne pouvez pas passer une séquence vide dans max
ou min
:
min([])
ValueError: min () arg est une séquence vide
Cependant, avec Python 3, vous pouvez transmettre l'argument default
avec une valeur qui sera renvoyée si la séquence est vide, au lieu de générer une exception:
max([], default=42)
# Output: 42
max([], default=0)
# Output: 0
Cas particulier: dictionnaires
Le minimum ou le maximum ou l'utilisation de sorted
dépend des itérations sur l'objet. Dans le cas de dict
, l'itération est uniquement sur les clés:
adict = {'a': 3, 'b': 5, 'c': 1}
min(adict)
# Output: 'a'
max(adict)
# Output: 'c'
sorted(adict)
# Output: ['a', 'b', 'c']
Pour conserver la structure du dictionnaire, vous devez parcourir le .items()
:
min(adict.items())
# Output: ('a', 3)
max(adict.items())
# Output: ('c', 1)
sorted(adict.items())
# Output: [('a', 3), ('b', 5), ('c', 1)]
Pour sorted
, vous pouvez créer un OrderedDict
pour conserver le tri tout en ayant une structure de type dict
:
from collections import OrderedDict
OrderedDict(sorted(adict.items()))
# Output: OrderedDict([('a', 3), ('b', 5), ('c', 1)])
res = OrderedDict(sorted(adict.items()))
res['a']
# Output: 3
Par valeur
Là encore, cela est possible en utilisant l'argument key
:
min(adict.items(), key=lambda x: x[1])
# Output: ('c', 1)
max(adict.items(), key=operator.itemgetter(1))
# Output: ('b', 5)
sorted(adict.items(), key=operator.itemgetter(1), reverse=True)
# Output: [('b', 5), ('a', 3), ('c', 1)]
Obtenir une séquence triée
En utilisant une séquence:
sorted((7, 2, 1, 5)) # tuple
# Output: [1, 2, 5, 7]
sorted(['c', 'A', 'b']) # list
# Output: ['A', 'b', 'c']
sorted({11, 8, 1}) # set
# Output: [1, 8, 11]
sorted({'11': 5, '3': 2, '10': 15}) # dict
# Output: ['10', '11', '3'] # only iterates over the keys
sorted('bdca') # string
# Output: ['a','b','c','d']
Le résultat est toujours une nouvelle list
; les données d'origine restent inchangées.
Minimum et Maximum d'une séquence
Obtenir le minimum d'une séquence (itérable) équivaut à accéder au premier élément d'une séquence sorted
:
min([2, 7, 5])
# Output: 2
sorted([2, 7, 5])[0]
# Output: 2
Le maximum est un peu plus compliqué, car sorted
garde l'ordre et max
renvoie la première valeur rencontrée. S'il n'y a pas de doublons, le maximum est le même que le dernier élément du retour trié:
max([2, 7, 5])
# Output: 7
sorted([2, 7, 5])[-1]
# Output: 7
Mais pas si plusieurs éléments sont évalués comme ayant la valeur maximale:
class MyClass(object):
def __init__(self, value, name):
self.value = value
self.name = name
def __lt__(self, other):
return self.value < other.value
def __repr__(self):
return str(self.name)
sorted([MyClass(4, 'first'), MyClass(1, 'second'), MyClass(4, 'third')])
# Output: [second, first, third]
max([MyClass(4, 'first'), MyClass(1, 'second'), MyClass(4, 'third')])
# Output: first
Tout élément contenant des éléments pouvant supporter <
ou >
opérations est autorisé.
Rendre les classes personnalisées ordonnables
min
, max
et sorted
doivent tous les objets être ordonnables. Pour être correctement classable, la classe doit définir toutes les 6 méthodes __lt__
, __gt__
, __ge__
, __le__
, __ne__
et __eq__
:
class IntegerContainer(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return "{}({})".format(self.__class__.__name__, self.value)
def __lt__(self, other):
print('{!r} - Test less than {!r}'.format(self, other))
return self.value < other.value
def __le__(self, other):
print('{!r} - Test less than or equal to {!r}'.format(self, other))
return self.value <= other.value
def __gt__(self, other):
print('{!r} - Test greater than {!r}'.format(self, other))
return self.value > other.value
def __ge__(self, other):
print('{!r} - Test greater than or equal to {!r}'.format(self, other))
return self.value >= other.value
def __eq__(self, other):
print('{!r} - Test equal to {!r}'.format(self, other))
return self.value == other.value
def __ne__(self, other):
print('{!r} - Test not equal to {!r}'.format(self, other))
return self.value != other.value
Bien que l'implémentation de toutes ces méthodes semble inutile, en omettre certaines rendra votre code sujet aux bogues .
Exemples:
alist = [IntegerContainer(5), IntegerContainer(3),
IntegerContainer(10), IntegerContainer(7)
]
res = max(alist)
# Out: IntegerContainer(3) - Test greater than IntegerContainer(5)
# IntegerContainer(10) - Test greater than IntegerContainer(5)
# IntegerContainer(7) - Test greater than IntegerContainer(10)
print(res)
# Out: IntegerContainer(10)
res = min(alist)
# Out: IntegerContainer(3) - Test less than IntegerContainer(5)
# IntegerContainer(10) - Test less than IntegerContainer(3)
# IntegerContainer(7) - Test less than IntegerContainer(3)
print(res)
# Out: IntegerContainer(3)
res = sorted(alist)
# Out: IntegerContainer(3) - Test less than IntegerContainer(5)
# IntegerContainer(10) - Test less than IntegerContainer(3)
# IntegerContainer(10) - Test less than IntegerContainer(5)
# IntegerContainer(7) - Test less than IntegerContainer(5)
# IntegerContainer(7) - Test less than IntegerContainer(10)
print(res)
# Out: [IntegerContainer(3), IntegerContainer(5), IntegerContainer(7), IntegerContainer(10)]
sorted
avec reverse=True
utilise également __lt__
:
res = sorted(alist, reverse=True)
# Out: IntegerContainer(10) - Test less than IntegerContainer(7)
# IntegerContainer(3) - Test less than IntegerContainer(10)
# IntegerContainer(3) - Test less than IntegerContainer(10)
# IntegerContainer(3) - Test less than IntegerContainer(7)
# IntegerContainer(5) - Test less than IntegerContainer(7)
# IntegerContainer(5) - Test less than IntegerContainer(3)
print(res)
# Out: [IntegerContainer(10), IntegerContainer(7), IntegerContainer(5), IntegerContainer(3)]
Mais sorted
peut utiliser __gt__
place si la valeur par défaut n’est pas implémentée:
del IntegerContainer.__lt__ # The IntegerContainer no longer implements "less than"
res = min(alist)
# Out: IntegerContainer(5) - Test greater than IntegerContainer(3)
# IntegerContainer(3) - Test greater than IntegerContainer(10)
# IntegerContainer(3) - Test greater than IntegerContainer(7)
print(res)
# Out: IntegerContainer(3)
Les méthodes de tri TypeError
une TypeError
si ni __lt__
ni __gt__
sont implémentées:
del IntegerContainer.__gt__ # The IntegerContainer no longer implements "greater then"
res = min(alist)
TypeError: types non ordonnables: IntegerContainer () <IntegerContainer ()
functools.total_ordering
décorateur peut être utilisé pour simplifier l’écriture de ces riches méthodes de comparaison. Si vous décorez votre classe avec total_ordering
, vous devez implémenter __eq__
, __ne__
et un seul des __lt__
, __le__
, __ge__
ou __gt__
, et le décorateur remplira le reste:
import functools
@functools.total_ordering
class IntegerContainer(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return "{}({})".format(self.__class__.__name__, self.value)
def __lt__(self, other):
print('{!r} - Test less than {!r}'.format(self, other))
return self.value < other.value
def __eq__(self, other):
print('{!r} - Test equal to {!r}'.format(self, other))
return self.value == other.value
def __ne__(self, other):
print('{!r} - Test not equal to {!r}'.format(self, other))
return self.value != other.value
IntegerContainer(5) > IntegerContainer(6)
# Output: IntegerContainer(5) - Test less than IntegerContainer(6)
# Returns: False
IntegerContainer(6) > IntegerContainer(5)
# Output: IntegerContainer(6) - Test less than IntegerContainer(5)
# Output: IntegerContainer(6) - Test equal to IntegerContainer(5)
# Returns True
Notez que le >
( supérieur à ) finit maintenant par appeler la méthode less than et, dans certains cas, même la méthode __eq__
. Cela signifie également que si la vitesse est très importante, vous devez implémenter vous-même chaque méthode de comparaison enrichie.
Extraire N plus grand ou N plus petit élément d’une
Pour trouver un nombre (plus d'un) de la plus grande ou de la plus petite valeur d'une itération, vous pouvez utiliser le nlargest
et le nsmallest
du module heapq
:
import heapq
# get 5 largest items from the range
heapq.nlargest(5, range(10))
# Output: [9, 8, 7, 6, 5]
heapq.nsmallest(5, range(10))
# Output: [0, 1, 2, 3, 4]
Ceci est beaucoup plus efficace que de trier la totalité des itérables puis de les couper de la fin ou du début. En interne, ces fonctions utilisent la structure de données de la file d'attente du segment de mémoire binaire , ce qui est très efficace pour ce cas d'utilisation.
Comme min
, max
et sorted
, ces fonctions acceptent l'option key
argument de mot - clé, qui doit être une fonction qui, étant donné un élément, renvoie sa clé de tri.
Voici un programme qui extrait 1000 lignes les plus longues d'un fichier:
import heapq
with open(filename) as f:
longest_lines = heapq.nlargest(1000, f, key=len)
Ici, nous ouvrons le fichier et transmettons le nlargest
fichier f
à la plus grande nlargest
. Itérer le fichier donne à chaque ligne du fichier une chaîne distincte; nlargest
passe ensuite chaque élément (ou ligne) est passé à la fonction len
pour déterminer sa clé de tri. len
, étant donné une chaîne, renvoie la longueur de la ligne en caractères.
Cela ne nécessite que le stockage pour une liste de 1000 lignes les plus grandes à ce jour, qui peut être comparée à
longest_lines = sorted(f, key=len)[1000:]
qui devra contenir tout le fichier en mémoire .