Python: Tuples / dictionnaires comme clés, sélectionner, trier

supposons que j'ai des quantités de fruits de différentes couleurs, par exemple, 24 bananes bleues, 12 pommes vertes, 0 fraises bleues et ainsi de suite. J'aimerais 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 tuples comme clés, par exemple,

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

ou même dictionnaires, p.ex.

{ {'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 bananes de toutes les couleurs, par exemple, ou pour trier ce dictionnaire par le nom du fruit. Est-il possible de le faire de manière propre?

il se pourrait bien que les dictionnaires avec des tuples comme clés ne soient pas la bonne façon de gérer cette situation.

toutes les suggestions sont les bienvenues!

82
demandé sur Nico Schlömer 2011-02-02 22:24:11

8 réponses

personnellement, une des choses que j'aime chez 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 supporter du dict de tuples pour l'implémentation de tableaux 2d, au moins quand quelque chose comme numpy ou une base de données n'est pas plus approprié. Bref, je pense que vous avez une bonne approche.

notez que vous ne pouvez pas utiliser les dicts comme des clés dans un dict sans faire un peu de travail supplémentaire, donc ce n'est 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 fruitcount dict:

>>> 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')]

Echoing chmullig, pour obtenir une liste de toutes les couleurs d'un fruit, vous devriez filtrer les clés ,i.e.

bananas = [fruit for fruit in fruits if fruit.name=='banana']
125
répondu senderle 2012-05-03 19:34:52

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 liste simple et les trier/les récupérer de la façon que vous souhaitez.

dans 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)

alors vous pouvez simplement construire des instances "fruits" 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 simple liste fruits sera beaucoup plus facile, moins confus, et mieux entretenu.

quelques exemples d'utilisation:

toutes les sorties ci-dessous est le résultat après l'exécution de l'extrait de code donné suivi 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

en ordre alphabétique 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
répondu Cuga 2011-02-02 20:34:57

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

" il se pourrait bien que les dictionnaires avec des tuples 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 trop pour l'OP;"

Ouais! Je pensais

donc, à mon avis, une liste de tuples est suffisante:

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
répondu eyquem 2013-07-02 13:05:29

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 facile serait sqlite . Vous pouvez garder le tout en mémoire en passant dans la chaîne de caractères ':memory:' au lieu d'un nom de fichier.

si vous voulez continuer sur cette voie, vous pouvez le faire avec les attributs supplémentaires dans la clé ou la valeur. Toutefois, un dictionnaire ne peut pas être la clé d'une un autre dictionnaire, mais un n-uplet peut. Les docs expliquer ce qui est admissible. Ce doit être un objet immuable, qui comprend des chaînes, des nombres et des tuples qui ne contiennent que des chaînes et des nombres (et plus de tuples 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 ceci:

#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')]
12
répondu chmullig 2011-02-02 19:45:52

avec les touches comme tuples, vous filtrez juste les touches avec le deuxième composant donné et le 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

Tri fonctionne parce que les tuples ont l'ordre naturel si leurs composants ont l'ordre naturel.

avec les touches comme des objets plutôt à part entière, vous filtrez juste par k.color == 'blue' .

vous ne pouvez pas vraiment utiliser les dicts comme clés, mais vous pouvez créer une classe la plus simple comme class Foo(object): pass et y ajouter des attributs sur le fly:

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

ces instances peuvent servir de clés dict, mais attention à leur mutabilité!

4
répondu 9000 2011-02-02 20:03:10

vous pourriez avoir un dictionnaire où 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": [{"jaunes": 24}], "pomme": [{"rouge": 12}, {"verte": 14}]}

Edit: comme eumiro l'a souligné, vous pouvez utiliser un dictionnaire de dictionnaires:

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

sortie:

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

3
répondu GreenMatt 2011-02-02 19:49:23

ce type de données est efficacement tiré d'une structure de données de type Trie. Il permet également un tri rapide. L'efficacité de mémoire peuvent ne pas être si grande.

un tri traditionnel stocke chaque lettre d'un mot comme un noeud dans l'arbre. Mais dans votre cas, votre "alphabet" est différent. Vous stockez des chaînes de caractères au lieu de caractères.

il pourrait ressembler à quelque chose comme ceci:

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

voir ce lien: trie en python

2
répondu Scott Morken 2017-05-23 12:02:16

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

  1. stocker les données de façon redondante avec deux dicts comme {'banana' : {'blue' : 4, ...}, .... } et {'blue': {'banana':4, ...} ...} . Ensuite, la recherche et le tri est facile, mais vous devez vous assurer que vous modifiez les dicts ensemble.

  2. stocker juste un dict, puis écrire des fonctions qui itèrent sur eux 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
répondu highBandWidth 2013-12-07 19:38:44