Python:元组/字典作为键、选择、排序

Posted

技术标签:

【中文标题】Python:元组/字典作为键、选择、排序【英文标题】:Python: Tuples/dictionaries as keys, select, sort 【发布时间】:2011-06-20 05:11:41 【问题描述】:

假设我有大量不同颜色的水果,例如,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,
    # ...

例如,我想检索所有蓝色水果或所有颜色的香蕉的列表,或者按水果名称对字典进行排序。有没有办法以干净的方式做到这一点?

很可能以元组为键的字典不是处理这种情况的正确方法。

欢迎所有建议!

【问题讨论】:

听起来你想要一个数据库... 您最好定义一个 clsas 来对这些数据建模,而不是尝试协调这些值的不同集合 @AdamRosenfield 也许他正在建造一个。 只是想补充一点,字典是不可散列的,所以你问的第二种语法是不可能的,因为 'fruit': 'banana', 'color': 'blue' 这是一个字典不能用作另一个字典的键。它会导致 TypeError: unhashable type: 'dict'。 【参考方案1】:

你想独立使用两个键,所以你有两个选择:

    使用两个字典冗余存储数据,分别为 'banana' : 'blue' : 4, ..., .... 'blue': 'banana':4, ... ...。然后,搜索和排序很容易,但您必须确保一起修改字典。

    只存储一个字典,然后编写迭代它们的函数,例如:

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

【讨论】:

我无法弄清楚为什么我的答案中的代码没有以正确的格式显示。我已经尝试将最后两行编辑并标记为代码,但它不起作用! 您已经创建了一个编号列表,并且解析器将代码(缩进 4 个空格)解释为该列表第二项的延续。将代码再缩进4个空格,共8个,解析器会将代码识别为代码并正确格式化。【参考方案2】:

数据库、字典字典、字典列表字典、命名元组(它是一个子类)、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

【讨论】:

您好,我喜欢您的解决方案,但它没有解决操作复杂性的问题。所有搜索类型都是列表大小的线性 ( O(n) )。虽然 OP 希望让某些操作比其他操作更快是有道理的(例如,我希望在 O(1) 中获得黄色香蕉计数是可能的。【参考方案3】:

这种类型的数据是从类似 Trie 的数据结构中有效提取的。它还允许快速排序。不过内存效率可能没那么高。

传统的 trie 将单词的每个字母存储为树中的一个节点。但在你的情况下,你的“字母”是不同的。您正在存储字符串而不是字符。

它可能看起来像这样:

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

查看此链接:trie in python

【讨论】:

【参考方案4】:

就我个人而言,我喜欢 python 的一件事是 tuple-dict 组合。你在这里实际上是一个二维数组(其中 x = 水果名称和 y = 颜色),我通常是用于实现二维数组的元组字典的支持者,至少在 numpy 或数据库之类的东西不是时更合适。所以简而言之,我认为你有一个很好的方法。

请注意,如果不做一些额外的工作,您不能将 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']

【讨论】:

#senderle 您作为对另一个答案的评论写道“但我的直觉是,数据库对于 OP 的需求来说太过分了;”;所以你更喜欢创建一个 namedtuple 子类。但是,如果不是具有自己的工具来处理数据的微型数据库,那么类的实例还有什么? 我可以从这些提取子列表中使用name='banana'吗? 正如 chmullig 指出的那样,您必须过滤密钥,即 bananas = filter(lambda fruit: fruit.name=='banana', fruits)bananas = [fruit for fruit in fruits if fruit.name=='banana']。这是嵌套字典可能更有效的一种方式;这一切都取决于您计划使用数据的方式。 在命名元组中添加更多键不会让事情变得更容易吗?我会说添加一个新属性count【参考方案5】:

您最好的选择是创建一个简单的数据结构来模拟您所拥有的。然后,您可以将这些对象存储在一个简单的列表中,并按您希望的方式对它们进行排序/检索。

对于这种情况,我会使用以下类:

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 的简单列表将更容易、更少混乱且维护得更好。

一些使用示例:

下面的所有输出都是运行给定代码 sn-p 后的结果:

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

【讨论】:

【参考方案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'

这些实例可以用作字典键,但要注意它们的可变性!

【讨论】:

【参考方案7】:

你可以有一个字典,其中的条目是其他字典的列表:

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]

编辑:正如 eumiro 指出的,您可以使用字典词典:

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

输出:

'香蕉': '黄色': 24, '苹果': '绿色':14,'红色':12

【讨论】:

字典列表的字典?也许字典就足够了? @eumiro:谢谢,你是对的,这是我最初的想法。但是,我在编写原始示例时将其变成了字典列表的字典。我添加了一个 dicts 示例。 嵌套字典容易混淆。请看我的回答 @Cuga:我同意 dicts 的 dicts 等可能会让人感到困惑。我只是提供一个说明性示例来回答@Nico 的问题。 我很抱歉:我并不是要暗示您的解决方案是错误的;它显然有效,在某些情况下它可能是理想的。我想分享我对这种情况的看法。【参考方案8】:

在这种情况下,您可能不应该使用字典。功能更全的库将是更好的选择。可能是一个真正的数据库。最简单的是sqlite。您可以通过传入字符串 ':memory:' 而不是文件名来将整个内容保存在内存中。

如果您确实想沿着这条路继续下去,您可以使用键或值中的额外属性来实现。但是,字典不能是另一个字典的键,但元组可以。 The docs 解释什么是允许的。它必须是一个不可变对象,包括字符串、数字和只包含字符串和数字的元组(以及更多只包含这些类型的元组递归......)。

你可以用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')]

【讨论】:

我没有也不会反对这个答案,因为在更大规模的情况下,数据库是(显然!)最好的方法。但我的直觉是,数据库对于 OP 的需求来说太过分了。也许这可以解释否决票?

以上是关于Python:元组/字典作为键、选择、排序的主要内容,如果未能解决你的问题,请参考以下文章

Javascript:使用元组作为字典键

使用要设置的字符串元组更新 Python 字典(键、值)失败

python的元组能作为字典的key吗?为什么?

python的元组能作为字典的key吗?为什么?

python的元组能作为字典的key吗?为什么?

Python字典,作为不增加值的元组键