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,
  ...
}

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

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

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

8 ответов


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

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

тем не менее, вы также должны рассмотреть 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']

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

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

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)

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

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

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

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

" мое чутье подсказывает, что база данных избыточна для нужд ОП;"

да! Я думал

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

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

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

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

вы можете сделать свой первый пример с 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')]

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

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

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

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

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


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

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}]}

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

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


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

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

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

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

смотрите здесь: trie в python


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

  1. хранения данных с избыточностью с двумя предсказывает как {'banana' : {'blue' : 4, ...}, .... } и {'blue': {'banana':4, ...} ...}. Затем поиск и сортировка легко, но вы должны убедиться, что вы изменить диктовки вместе.

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

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