Python: кортежи / словари в качестве ключей, выбор, сортировка - PullRequest
90 голосов
/ 02 февраля 2011

Предположим, у меня есть фрукты разных цветов, например, 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 ]

133 голосов
/ 02 февраля 2011

Лично одна из вещей, которые мне нравятся в 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 = {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']
18 голосов
/ 02 февраля 2011

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

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

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
17 голосов
/ 03 февраля 2011

База данных, dict of dicts, словарь списка словарей, названный кортеж (это подкласс), 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
13 голосов
/ 02 февраля 2011

Словарь, вероятно, не то, что вы должны использовать в этом случае.Более полнофункциональная библиотека была бы лучшей альтернативой.Вероятно, настоящая база данных.Самый простой будет 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')]
4 голосов
/ 02 февраля 2011

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

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, но остерегайтесь их изменчивости!

3 голосов
/ 02 февраля 2011

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

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

Выход:

{'банан': [{'yellow': 24}], 'apple': [{'red': 12}, {'green': 14}]}

Редактировать: Как указывал eumiro, вы можете использовать словарь словарей:

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

Выход:

{'банан': {'желтый': 24}, 'яблоко': {'зеленый': 14, 'красный': 12}}

2 голосов
/ 19 апреля 2013

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

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

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

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

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

2 голосов
/ 02 февраля 2011

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

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