Python: кортежи/словари в качестве ключей, выберите, отсортируйте

Предположим, что у меня есть количество фруктов разных цветов, например, 24 синих банана, 12 зеленых яблок, 0 голубых клубников и так далее. Я хотел бы организовать их в структуре данных в Python, что позволяет легко выбирать и сортировать. Моя идея заключалась в том, чтобы поместить их в словарь с кортежами в качестве ключей, например,

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

или даже словари, например,

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

Я хотел бы получить список всех синих фруктов или бананов всех цветов, например, или отсортировать этот словарь по названию фрукта. Есть ли способы сделать это чистым способом?

Вполне возможно, что словари с кортежами в качестве ключей не являются надлежащим способом справиться с этой ситуацией.

Приветствуем все предложения!

Ответ 1

Лично одна из вещей, которые мне нравятся в python, - это комбинация кортежей-диктов. То, что у вас здесь, представляет собой массив 2d (где x = имя фрукта и y = цвет), и я, как правило, сторонник dict кортежей для реализации 2d массивов, по крайней мере, когда что-то вроде numpy или базы данных isn ' t более уместным. Короче говоря, я думаю, что у вас есть хороший подход.

Обратите внимание, что вы не можете использовать dicts как ключи в dict без дополнительной работы, так что это не очень хорошее решение.

Тем не менее, вы должны также рассмотреть namedtuple(). Таким образом вы можете сделать это:

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

Теперь вы можете использовать свой файл fruitcount dict:

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

Другие трюки:

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

Повторяя chmullig, чтобы получить список всех цветов одного плода, вам придется отфильтровать клавиши, т.е.

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

Ответ 2

Ваш лучший вариант - создать простую структуру данных для моделирования того, что у вас есть. Затем вы можете хранить эти объекты в простом списке и сортировать/извлекать их любым способом.

В этом случае я бы использовал следующий класс:

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)

Затем вы можете просто создать экземпляры "Fruit" и добавить их в список, как показано ниже:

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

Простой список fruits будет намного проще, менее запутанным и лучше поддерживается.

Некоторые примеры использования:

Все выходы, приведенные ниже, являются результатом после выполнения заданного фрагмента кода, а затем:

for fruit in fruits:
    print fruit

Не отсортированный список:

Вывод:

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

Отсортировано по алфавиту по имени:

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

Вывод:

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

Отсортировано по количеству:

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

Вывод:

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

Где цвет == красный:

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

Вывод:

Name: apple, Color: red, Quantity: 12

Ответ 3

База данных, dict of dicts, словарь списка словарей, названный кортеж (это подкласс), sqlite, резервирование... Я не поверила своим глазам. Что еще?

"Вполне возможно, что словари с кортежами в качестве ключей не являются правильным способом справиться с этой ситуацией".

"мое чувство кишки состоит в том, что база данных слишком много для потребностей OP";

Да! Я думал, что

Итак, на мой взгляд, списка кортежей достаточно много:

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

результат

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

Ответ 4

Словарь, вероятно, не тот, который вы должны использовать в этом случае. Лучшей альтернативой станет более полнофункциональная библиотека. Вероятно, настоящая база данных. Самый простой способ - sqlite. Вы можете сохранить все это в памяти, передав в строке ': memory:' вместо имени файла.

Если вы хотите продолжить этот путь, вы можете сделать это с дополнительными атрибутами в ключе или значении. Однако словарь не может быть ключом к другому словарю, но кортеж может. Документы объясняют, что допустимо. Он должен быть неизменным объектом, который включает в себя строки, числа и кортежи, которые содержат только строки и числа (и больше кортежей, содержащих только эти типы, рекурсивно...).

Вы можете сделать свой первый пример с d = {('apple', 'red') : 4}, но будет очень сложно запросить то, что вы хотите. Вам нужно будет сделать что-то вроде этого:

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

Ответ 5

У вас может быть словарь, в котором записи представляют собой список других словарей:

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

Вывод:

{'banana': [{'yellow': 24}], 'apple': [{'red': 12}, {'green': 14}]}

Изменить: Как отметил эумиро, вы можете использовать словарь словарей:

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

Вывод:

{'banana': {'yellow': 24}, 'apple': {'green': 14, 'red': 12}}

Ответ 6

С помощью ключей как кортежей вы просто отфильтровываете ключи с заданным вторым компонентом и сортируете его:

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

Сортировка работает, потому что кортежи имеют естественный порядок, если их компоненты имеют естественный порядок.

С помощью клавиш, как полнофункциональных объектов, вы просто фильтруете k.color == 'blue'.

Вы не можете использовать dicts как ключи, но вы можете создать простейший класс, например class Foo(object): pass, и добавить к нему любые атрибуты "на лету":

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

Эти экземпляры могут служить в качестве ключей dict, но остерегайтесь их изменчивости!

Ответ 7

Вы хотите использовать два ключа самостоятельно, поэтому у вас есть два варианта:

  • Сохраняйте данные избыточно с двумя dicts как {'banana' : {'blue' : 4, ...}, .... } и {'blue': {'banana':4, ...} ...}. Затем поиск и сортировка просты, но вы должны убедиться, что вы модифицируете dicts вместе.

  • Сохраните его только один dict, а затем напишите функции, которые перебирают их, например.:

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

Ответ 8

Этот тип данных эффективно извлекается из структуры данных, подобной Trie. Это также позволяет быстро сортировать. Эффективность памяти может быть не такой уж большой.

Традиционное trie хранит каждую букву слова как node в дереве. Но в вашем случае ваш "алфавит" отличается. Вы сохраняете строки вместо символов.

он может выглядеть примерно так:

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

см. эту ссылку: trie в python