在什么情况下我会使用元组作为字典键?


77

我正在研究列表和元组之间区别(在Python中)。一个明显的事实是,元组是不可变的(在初始赋值后不能更改值),而列表是可变的。

文章中的一句话让我明白了:

只能将不可变元素用作字典键,因此只能将元组而不是列表用作键。

我很难考虑要使用元组作为字典键的情况。您能否提供一个示例问题,说明这是自然,高效,优雅或显而易见的解决方案?

编辑:

感谢您的示例。到目前为止,我认为一个非常重要的应用程序是函数值的缓存。


2
您可以使用元组,但只能使用具有不可变元素的元组。如果一个元组包含一个列表(作为其元素之一),则该元组不能用作键。基本规则是数据(tupple)必须是可哈希的。
pepr 2012年

Answers:


109

经典示例:您要将点值存储为(x,y)的元组


4
哇。这是真的。我想不出任何其他有效存储函数值的方法!如果函数评估非常昂贵,则只需执行一次,然后存储这些点以供以后检索。+1!谢谢!
Escualo

4
同意 同样,在内存中处理内容的任何地方,您都将使用复合键来处理关系数据库中的相同内容。
乔·马贝尔

我现在正在实现完全相同的方案。搜索检索的效率如何?是O(1)还是O(N)?
shiv

28
salaries = {}
salaries[('John', 'Smith')] = 10000.0
salaries[('John', 'Parker')] = 99999.0

编辑1 当然可以salaries['John Smith'] = whatever,但是您必须做一些额外的工作才能将密钥分为名字和姓氏。那么pointColor[(x, y, z)] = "red",这里元组键的好处更加突出。

我必须强调,这不是最佳做法。在许多情况下,您最好创建特殊的类来处理类似的情况,但是Arrieta要求提供示例,我给了她(他)。

编辑0

顺便说一下,每个元组元素也必须是可哈希的:

>>> d = {}
>>> t = (range(3), range(10, 13))
>>> d[t] = 11
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable
>>>

2
我不使用python太多,但是薪水[('John Smith')] = 99998不会是有效的字典键吗?
GrayWizardx

1
我同意您可以执行此操作,但是我将使用带有__init __(self,Firs,Last,Salary)的Employee类对该数据进行建模,并为列表中的每个元素创建一个实例。在这种情况下,使用“元组作为键”把戏对我来说似乎有点不自然。你怎么看?
Escualo

1
当然会。但是,如果您想将密钥拆分为名字和姓氏,那么您将不得不做额外的工作。
鲍里斯·哥列里克

1
谢谢您的编辑,我认为颜色示例非常好,就像dict[tuple] = f(tuple)答案中提到的一般。顺便说一句,我是“他” :)
Escualo

4
请注意,您不需要括号,元组由逗号定义。salaries['John', 'Smith'] = 10000.0也会工作:)
Heretron

8

我用元组很多时间作为dict关键,例如

  • 当必须从多个值创建唯一键时,我会使用它们

    根据first_namelast_namekey可能是key =,'%s_%s'%(first_name, last_name)但是更好的方法是key = (first_name, last_name)因为

    1. 它更具可读性,更短且计算量更少
    2. 检索单个值更容易
    3. 最重要的key = '%s_%s'%(first_name, last_name)是错误的,可能无法为的所有值提供唯一键first_namelast_name例如,当值包含_
  • 缓存函数的结果

    def func(a1, b1):
        if (a1,b1) in cache: return cache[(a1,b1)]
        ...
    

5

我在应用程序中使用元组作为字典键,该应用程序按地理位置比较网络设备。由于设备在每个位置的命名都相似,因此它提供了一种自然的方式来知道在处理倍数时是否已经看到与该配对匹配的设备。

seen = {}
seen[('abc', 'lax')] = 1
seen[('xyz', 'nyc')] = 1

5

当要显示在一起构成一个键的多个元素时,可以使用元组作为键。

例如: {(<x-coordinate>,<y-coordinate>): <indicating letter>}

在这里,如果我们单独使用x-coordinatey-coordinate单独使用,我们将不会代表这一点。


5

在机器学习和深度学习的上下文中,如果您正在执行超参数搜索以寻找最佳的超参数,那么使用元组作为键绝对是非常有用的。

比方说,你正在寻找最佳结合超参数learning_rateregularization_factormodel_complexity

然后,您可以在Python中拥有一个字典,在其中您可以将这些hparams用作并将训练算法中它们对应的权重矩阵作为的不同组合

hparams_hist = {}
hparams_hist[(0.001, 0.7, 5)] = weight_matrix1
hparams_hist[(0.0001, 0.8, 2)] = weight_matrix2

进一步需要这些权重矩阵进行实时预测。


1
我发现自己经常这样做!很好的例子。
ruancomelli


2

我想在排序的情况下,使用元组可能有好处。例如,假设字典键代表一个排序字段(显然会有一个默认的排序字段来防止该键被None)。如果需要多个排序字段,例如按姓氏,然后按名字排序的情况,将元组用作字典键不是一个好主意吗?

当然,这样的想法可能用途有限,但这并不意味着它完全没有用。


2

如果您要构建基本的分析工具,则可以将其用于渠道分析。

例如,计算将鼠标悬停在text2上之后有多少人点击了image3。

    funnels = defaultdict(int)
    funnels[('hovered_text2', 'clicked_image3')] += 1

2

您可以将其用于搜索空间中某个点的近似恒定时间搜索。例如,您可以将其用于约束满足问题,其中每个元组可能包含一些约束。约束的形式可能是(v1.v2),其中color(v1)!= color(v2)用于对概率进行着色等。使用元组作为字典键,您将能够在恒定时间内判断排列是否满足约束。 。


1
def getHash(word):
    result={}
    for i in range(len(word)):
        if word[i] in result:
            result[word[i]]+=1
        else :
            result[word[i]]=1

    return tuple (sorted((result.items())))


def groupAnagrams(words):
    resultHash={}
    for i in range(len(words)):
        s=getHash(words[i].lower())
        #print s
        if s in resultHash :
            l=list(resultHash[s]) 
            l.append(words[i])
            resultHash[s] = l # list(resultHash[s]).append(words[i])  
        else :
            resultHash[s]=[words[i]] # Creating list 

    return resultHash.values()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.