web-dev-qa-db-fra.com

Python: Touples / dictionnaires en tant que clés, sélectionnez, triez

supposons que je dispose de quantités de fruits de différentes couleurs, par exemple 24 bananes bleues, 12 pommes vertes, 0 fraises bleues, etc. Je voudrais les organiser dans une structure de données en Python qui permet une sélection et un tri faciles. Mon idée était de les mettre dans un dictionnaire avec des n-uplets comme clés, par exemple,

{ ('banana',    'blue' ): 24,
  ('Apple',     'green'): 12,
  ('strawberry','blue' ): 0,
  ...
}

ou même des dictionnaires, par exemple,

{ {'fruit': 'banana',    'color': 'blue' }: 24,
  {'fruit': 'Apple',     'color': 'green'}: 12,
  {'fruit': 'strawberry','color': 'blue' }: 0,
  ...
}

J'aimerais récupérer une liste de tous les fruits bleus, ou de bananes de toutes les couleurs, par exemple, ou trier ce dictionnaire par le nom du fruit. Y a-t-il des moyens de le faire de manière propre?

Il se pourrait bien que les dictionnaires avec des n-uplets comme clés ne soient pas le moyen propre de gérer cette situation.

Toutes les suggestions sont les bienvenues!

92
Nico Schlömer

Personnellement, une des choses qui me passionne python est la combinaison Tuple-dict. Ce que vous avez ici est effectivement un tableau 2d (où x = nom du fruit et y = couleur), et je suis généralement un partisan du principe des tuples pour la mise en oeuvre de tableaux 2D, du moins lorsque quelque chose comme numpy ou une base de données n'est pas plus approprié.En bref, je pense que vous avez une bonne approche.

Notez que vous ne pouvez pas utiliser les touches comme clés dans un dict sans faire un travail supplémentaire, ce n'est donc pas une très bonne solution.

Cela dit, vous devriez également considérer namedtuple () . De cette façon, vous pourriez faire ceci:

>>> from collections import namedtuple
>>> Fruit = namedtuple("Fruit", ["name", "color"])
>>> f = Fruit(name="banana", color="red")
>>> print f
Fruit(name='banana', color='red')
>>> f.name
'banana'
>>> f.color
'red'

Maintenant, vous pouvez utiliser votre dictée Fruitcount:

>>> fruitcount = {Fruit("banana", "red"):5}
>>> fruitcount[f]
5

Autres astuces:

>>> fruits = fruitcount.keys()
>>> fruits.sort()
>>> print fruits
[Fruit(name='Apple', color='green'), 
 Fruit(name='Apple', color='red'), 
 Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue')]
>>> fruits.sort(key=lambda x:x.color)
>>> print fruits
[Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue'), 
 Fruit(name='Apple', color='green'), 
 Fruit(name='Apple', color='red')]

En écho à chmullig, pour obtenir la liste de toutes les couleurs d’un fruit, il vous faudrait filtrer les touches, c.-à-d.

bananas = [fruit for fruit in fruits if fruit.name=='banana']
135
senderle

Votre meilleure option sera de créer une structure de données simple pour modéliser ce que vous avez. Ensuite, vous pouvez stocker ces objets dans une simple liste et les trier/les récupérer comme vous le souhaitez.

Pour ce cas, j'utiliserais la classe suivante:

class Fruit:
    def __init__(self, name, color, quantity): 
        self.name = name
        self.color = color
        self.quantity = quantity

    def __str__(self):
        return "Name: %s, Color: %s, Quantity: %s" % \
     (self.name, self.color, self.quantity)

Ensuite, vous pouvez simplement construire des instances "Fruit" et les ajouter à une liste, comme indiqué de la manière suivante:

fruit1 = Fruit("Apple", "red", 12)
fruit2 = Fruit("pear", "green", 22)
fruit3 = Fruit("banana", "yellow", 32)
fruits = [fruit3, fruit2, fruit1] 

La liste simple fruits sera beaucoup plus facile, moins déroutante et mieux entretenue.

Quelques exemples d'utilisation:

Toutes les sorties ci-dessous sont le résultat après l'exécution de l'extrait de code donné, suivies de:

for fruit in fruits:
    print fruit

Liste non triée:

Affiche:

Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22
Name: Apple, Color: red, Quantity: 12

Trié alphabétiquement par nom:

fruits.sort(key=lambda x: x.name.lower())

Affiche:

Name: Apple, Color: red, Quantity: 12
Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22

Trié par quantité:

fruits.sort(key=lambda x: x.quantity)

Affiche:

Name: Apple, Color: red, Quantity: 12
Name: pear, Color: green, Quantity: 22
Name: banana, Color: yellow, Quantity: 32

Où couleur == rouge:

red_fruit = filter(lambda f: f.color == "red", fruits)

Affiche:

Name: Apple, Color: red, Quantity: 12
18
Cuga

Base de données, dict of dicts, dictionnaire de liste de dictionnaires, nommé Tuple (c'est une sous-classe), sqlite, redondance ... Je n'en croyais pas mes yeux. Quoi d'autre ?

"Il se pourrait bien que les dictionnaires avec des n-uplets comme clés ne soient pas la bonne façon de gérer cette situation."

"Mon sentiment est qu'une base de données est excessive pour les besoins du PO;"

Ouais! j'ai pensé

Donc, à mon avis, une liste de tuples suffit amplement:

from operator import itemgetter

li = [  ('banana',     'blue'   , 24) ,
        ('Apple',      'green'  , 12) ,
        ('strawberry', 'blue'   , 16 ) ,
        ('banana',     'yellow' , 13) ,
        ('Apple',      'gold'   , 3 ) ,
        ('pear',       'yellow' , 10) ,
        ('strawberry', 'orange' , 27) ,
        ('Apple',      'blue'   , 21) ,
        ('Apple',      'silver' , 0 ) ,
        ('strawberry', 'green'  , 4 ) ,
        ('banana',     'brown'  , 14) ,
        ('strawberry', 'yellow' , 31) ,
        ('Apple',      'pink'   , 9 ) ,
        ('strawberry', 'gold'   , 0 ) ,
        ('pear',       'gold'   , 66) ,
        ('Apple',      'yellow' , 9 ) ,
        ('pear',       'brown'  , 5 ) ,
        ('strawberry', 'pink'   , 8 ) ,
        ('Apple',      'purple' , 7 ) ,
        ('pear',       'blue'   , 51) ,
        ('chesnut',    'yellow',  0 )   ]


print set( u[1] for u in li ),': all potential colors'
print set( c for f,c,n in li if n!=0),': all effective colors'
print [ c for f,c,n in li if f=='banana' ],': all potential colors of bananas'
print [ c for f,c,n in li if f=='banana' and n!=0],': all effective colors of bananas'
print

print set( u[0] for u in li ),': all potential fruits'
print set( f for f,c,n in li if n!=0),': all effective fruits'
print [ f for f,c,n in li if c=='yellow' ],': all potential fruits being yellow'
print [ f for f,c,n in li if c=='yellow' and n!=0],': all effective fruits being yellow'
print

print len(set( u[1] for u in li )),': number of all potential colors'
print len(set(c for f,c,n in li if n!=0)),': number of all effective colors'
print len( [c for f,c,n in li if f=='strawberry']),': number of potential colors of strawberry'
print len( [c for f,c,n in li if f=='strawberry' and n!=0]),': number of effective colors of strawberry'
print

# sorting li by name of fruit
print sorted(li),'  sorted li by name of fruit'
print

# sorting li by number 
print sorted(li, key = itemgetter(2)),'  sorted li by number'
print

# sorting li first by name of color and secondly by name of fruit
print sorted(li, key = itemgetter(1,0)),'  sorted li first by name of color and secondly by name of fruit'
print

résultat

set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange', 'silver']) : all potential colors
set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange']) : all effective colors
['blue', 'yellow', 'brown'] : all potential colors of bananas
['blue', 'yellow', 'brown'] : all effective colors of bananas

set(['strawberry', 'chesnut', 'pear', 'banana', 'Apple']) : all potential fruits
set(['strawberry', 'pear', 'banana', 'Apple']) : all effective fruits
['banana', 'pear', 'strawberry', 'Apple', 'chesnut'] : all potential fruits being yellow
['banana', 'pear', 'strawberry', 'Apple'] : all effective fruits being yellow

9 : number of all potential colors
8 : number of all effective colors
6 : number of potential colors of strawberry
5 : number of effective colors of strawberry

[('Apple', 'blue', 21), ('Apple', 'gold', 3), ('Apple', 'green', 12), ('Apple', 'pink', 9), ('Apple', 'purple', 7), ('Apple', 'silver', 0), ('Apple', 'yellow', 9), ('banana', 'blue', 24), ('banana', 'brown', 14), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'blue', 51), ('pear', 'brown', 5), ('pear', 'gold', 66), ('pear', 'yellow', 10), ('strawberry', 'blue', 16), ('strawberry', 'gold', 0), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('strawberry', 'pink', 8), ('strawberry', 'yellow', 31)]   sorted li by name of fruit

[('Apple', 'silver', 0), ('strawberry', 'gold', 0), ('chesnut', 'yellow', 0), ('Apple', 'gold', 3), ('strawberry', 'green', 4), ('pear', 'brown', 5), ('Apple', 'purple', 7), ('strawberry', 'pink', 8), ('Apple', 'pink', 9), ('Apple', 'yellow', 9), ('pear', 'yellow', 10), ('Apple', 'green', 12), ('banana', 'yellow', 13), ('banana', 'brown', 14), ('strawberry', 'blue', 16), ('Apple', 'blue', 21), ('banana', 'blue', 24), ('strawberry', 'orange', 27), ('strawberry', 'yellow', 31), ('pear', 'blue', 51), ('pear', 'gold', 66)]   sorted li by number

[('Apple', 'blue', 21), ('banana', 'blue', 24), ('pear', 'blue', 51), ('strawberry', 'blue', 16), ('banana', 'brown', 14), ('pear', 'brown', 5), ('Apple', 'gold', 3), ('pear', 'gold', 66), ('strawberry', 'gold', 0), ('Apple', 'green', 12), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('Apple', 'pink', 9), ('strawberry', 'pink', 8), ('Apple', 'purple', 7), ('Apple', 'silver', 0), ('Apple', 'yellow', 9), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'yellow', 10), ('strawberry', 'yellow', 31)]   sorted li first by name of color and secondly by name of fruit
17
eyquem

Un dictionnaire n'est probablement pas ce que vous devriez utiliser dans ce cas. Une bibliothèque plus complète serait une meilleure alternative. Probablement une vraie base de données. Le plus simple serait sqlite . Vous pouvez garder le tout en mémoire en passant la chaîne ': memory:' au lieu d'un nom de fichier.

Si vous souhaitez continuer dans cette voie, vous pouvez le faire avec les attributs supplémentaires de la clé ou de la valeur. Cependant, un dictionnaire ne peut pas être la clé d'un autre dictionnaire, mais un tuple le peut. La documentation explique ce qui est permis. Ce doit être un objet immuable, qui comprend des chaînes, des nombres et des n-uplets qui ne contiennent que des chaînes et des nombres (et plusieurs n-uplets ne contenant que ces types récursivement ...).

Vous pouvez faire votre premier exemple avec d = {('Apple', 'red') : 4}, mais il sera très difficile de demander ce que vous voulez. Vous devez faire quelque chose comme ça:

#find all apples
apples = [d[key] for key in d.keys() if key[0] == 'Apple']

#find all red items
red = [d[key] for key in d.keys() if key[1] == 'red']

#the red Apple
redapples = d[('Apple', 'red')]
13
chmullig

Avec les clés comme nuplets, il vous suffit de filtrer les clés avec un second composant donné et de les trier:

blue_fruit = sorted([k for k in data.keys() if k[1] == 'blue'])
for k in blue_fruit:
  print k[0], data[k] # prints 'banana 24', etc

Le tri fonctionne car les n-uplets ont un ordre naturel si leurs composants ont un ordre naturel.

Avec les clés comme objets plutôt complets, il vous suffit de filtrer par k.color == 'blue'.

Vous ne pouvez pas vraiment utiliser les dict comme clés, mais vous pouvez créer une classe la plus simple telle que class Foo(object): pass et y ajouter n'importe quel attribut à la volée:

k = Foo()
k.color = 'blue'

Ces instances peuvent servir de clés de dictée, mais méfiez-vous de leur mutabilité!

4
9000

Vous pourriez avoir un dictionnaire dont les entrées sont une liste d'autres dictionnaires:

fruit_dict = dict()
fruit_dict['banana'] = [{'yellow': 24}]
fruit_dict['Apple'] = [{'red': 12}, {'green': 14}]
print fruit_dict

Sortie:

{'banane': [{'jaune': 24}], 'Apple': [{'rouge': 12}, {'vert': 14}]}

Edit: Comme le faisait remarquer eumiro, vous pourriez utiliser un dictionnaire de dictionnaires:

fruit_dict = dict()
fruit_dict['banana'] = {'yellow': 24}
fruit_dict['Apple'] = {'red': 12, 'green': 14}
print fruit_dict

Sortie:

{'banane': {'jaune': 24}, 'pomme': {'vert': 14, 'rouge': 12}}

3
GreenMatt

Vous voulez utiliser deux clés indépendamment, vous avez donc deux choix:

  1. Stockez les données de manière redondante avec deux dessins en tant que {'banana' : {'blue' : 4, ...}, .... } et {'blue': {'banana':4, ...} ...}. Ensuite, la recherche et le tri sont faciles, mais vous devez vous assurer de modifier les docs ensemble.

  2. Stockez-le juste un dict, puis écrivez des fonctions qui les parcourent, par exemple:

    d = {'banana' : {'blue' : 4, 'yellow':6}, 'Apple':{'red':1} }
    
    blueFruit = [(fruit,d[fruit]['blue']) if d[fruit].has_key('blue') for fruit in d.keys()]
    
2
highBandWidth

Ce type de données est efficacement extrait d'une structure de données de type Trie. Cela permet également un tri rapide. Cependant, l'efficacité de la mémoire n'est peut-être pas si grande.

Un tri traditionnel stocke chaque lettre d'un mot sous forme de nœud dans l'arborescence. Mais dans votre cas, votre "alphabet" est différent. Vous stockez des chaînes au lieu de caractères.

cela pourrait ressembler à quelque chose comme ça:

root:                Root
                     /|\
                    / | \
                   /  |  \     
fruit:       Banana Apple Strawberry
              / |      |     \
             /  |      |      \
color:     Blue Yellow Green  Blue
            /   |       |       \
           /    |       |        \
end:      24   100      12        0

voir ce lien: trie en python

2
Scott Morken