如何创建一个具有多个键对应一个值的字典?

88

我有一个关于字典的问题。我的目标是将多个键映射到同一个值,如下所示:

dictionary = {('a', 'b'): 1, ('c', 'd'): 2}
assert dictionary['a'] == 1
assert dictionary['b'] == 1

有任何想法吗?


@evil_inside,啊,如果这是你想要的,为什么不直接将1分配给两个键呢? - senderle
将相同的值分配给字典对象中的多个键是否可能同时进行?这可能会有所帮助。 - Seraph
@Eli,那不是重复内容。事实上,该链接是这个的重复。看一下帖子发布的时间。 - Thavas Antonio
6个回答

44

我猜你是指这个:

class Value:
    def __init__(self, v=None):
        self.v = v

v1 = Value(1)
v2 = Value(2)

d = {'a': v1, 'b': v1, 'c': v2, 'd': v2}
d['a'].v += 1

d['b'].v == 2 # True
  • Python中的字符串和数字是不可变对象,
  • 因此,如果您想让d['a']d['b']指向相同的值,并且在更改时“更新”,请将该值引用到一个可变对象(例如上面所述的用户自定义类或dictlistset)。
  • 然后,当您修改d['a']处的对象时,d['b']也会同时更改,因为它们都指向同一个对象。

你不必使用不可变对象,只需使用相同的变量(对象的引用)分配值,就可以获得可变和不可变对象的相同结果,例如 num = 2 而不是直接使用对象 (num 是变量,2 是对象)。您可以通过使用 is 关键字进行测试,例如:if d['a'] is d['b']: 。如果两个键都指向同一个对象,则表达式将为 True - Yomi
1
@Yomi 这种方法无法用于分配新的(不可变)对象!它们最初可能指向同一个对象(使用整数时,即使您在那里放置文字值)。但是尝试将 d['a'] 的值重新分配给另一个整数变量,并查看 d['b'] 是否已更改。无论您使用变量名称还是文字数字,因为它仅向一个字典条目分配新的(不可变)对象,而不是其他条目。它不知道哪个键具有与值相同的对象,哪个键也应该更改以指向另一个对象。 - xuiqzy

20
如果您需要经常添加到此字典中,则应采用基于类的方法,类似于@Latty在此SO问题2d-dictionary-with-many-keys-that-will-return-the-same-value中的答案。
但是,如果您有一个静态字典,并且只需要通过多个键访问值,则可以简单地使用两个字典的路线。一个用于存储别名键关联,另一个用于存储实际数据:
alias = {
    'a': 'id1',
    'b': 'id1',
    'c': 'id2',
    'd': 'id2'
}

dictionary = {
    'id1': 1,
    'id2': 2
}

dictionary[alias['a']]

如果您需要添加到字典中,您可以编写一个类似于以下函数的函数来使用两个字典:
def add(key, id, value=None)
    if id in dictionary:
        if key in alias:
            # Do nothing
            pass
        else:
            alias[key] = id
    else:
        dictionary[id] = value
        alias[key] = id

add('e', 'id2')
add('f', 'id3', 3)

尽管这样可以实现,但如果您想要像这样做些事情,编写自己的数据结构可能是更好的选择,尽管它可能使用类似的结构。

12

如果使用fromkeys,您的示例将创建多个键值对。如果不想这样做,可以使用一个键并为其创建别名。例如,如果您正在使用寄存器映射,则键可以是寄存器地址,而别名可以是寄存器名称。这样您就可以在正确的寄存器上执行读/写操作。

>>> mydict = {}
>>> mydict[(1,2)] = [30, 20]
>>> alias1 = (1,2)
>>> print mydict[alias1]
[30, 20]
>>> mydict[(1,3)] = [30, 30]
>>> print mydict
{(1, 2): [30, 20], (1, 3): [30, 30]}
>>> alias1 in mydict
True

6

很简单。首先,你需要了解Python解释器的设计。它基本上不会为所有变量分配内存,如果任何两个或更多的变量具有相同的值,则只会映射到该值。

我们来看代码示例:

In [6]: a = 10

In [7]: id(a)
Out[7]: 10914656

In [8]: b = 10

In [9]: id(b)
Out[9]: 10914656

In [10]: c = 11

In [11]: id(c)
Out[11]: 10914688

In [12]: d = 21

In [13]: id(d)
Out[13]: 10915008

In [14]: e = 11

In [15]: id(e)
Out[15]: 10914688

In [16]: e = 21

In [17]: id(e)
Out[17]: 10915008

In [18]: e is d
Out[18]: True
In [19]: e = 30

In [20]: id(e)
Out[20]: 10915296

从上面的输出可以看出,变量a和b共享同一内存,c和d具有不同的内存。当我创建一个新变量e并存储一个已经存在于变量c中的值(11),它会映射到该内存位置,并且不会创建新的内存。当我将变量e中的值更改为已经存在于变量d中的21时,现在变量d和e共享同一内存位置。最后,当我将变量e中的值更改为没有存储在任何其他变量中的30时,它会为e创建一个新的内存。
因此,具有相同值的任何变量都共享内存。
引用: 列表和字典对象除外。
现在来回答你的问题。
当多个键具有相同值时,所有键共享相同的内存,因此你所期望的东西已经在Python中实现了。
你可以简单地像这样使用它:
In [49]: dictionary = {
    ...:     'k1':1,
    ...:     'k2':1,
    ...:     'k3':2,
    ...:     'k4':2}
    ...:     
    ...:     

In [50]: id(dictionary['k1'])
Out[50]: 10914368

In [51]: id(dictionary['k2'])
Out[51]: 10914368

In [52]: id(dictionary['k3'])
Out[52]: 10914400

In [53]: id(dictionary['k4'])
Out[53]: 10914400

从上面的输出可以看出,键k1和k2映射到同一地址,这意味着值1只在内存中存储了一次,这就是你想要的多个键对应一个值的字典。 :P

4
我相信这只是因为Python会存储较小的数字对象,这样就不需要每次重新创建。如果你要存储更复杂的数据,比如较大的数字或字符串等,情况就不同了。 - Michael Murphy
1
这不是一个答案,也是一个错误的方向。它甚至不能做到 OP 想要的:dictionary['k2'] = 3 使 'k2' 映射到 3,而 'k1' 仍然映射到 1。这个 ID 的东西只是 Python 在幕后工作方式的一种好奇心。Python 不是 C,你不能用指针来瞎搞并期望它能正常工作。即使在可能按预期工作的地方(这里不是这种情况),它也绝对不应该在任何曾经远离生产代码的代码中使用。 - Neinstein

1
拥有多个键
#!/usr/bin/env python3

def get_keys(s):
    # Lower the user's entry to easily manipulate data
    s = s.lower()
    
    # Create a list to simulate multiple keys
    numbers = ['uno', 'one', 'um', 'eins', 'ein']
    
    # Lambda for input validation
    validator = lambda key: key if key in numbers else 'no-key-found'  # return me x if x is found in the list numbers, contratiwise return me 'no-key-found'
    
    dic = {
        validator(s):'1',
        'no-key-found':'Key does not exist'
    }
    
    
    return dic[validator(s)]


print(get_keys(input('Type in word: ')))

拥有动态密钥
#!/usr/bin/env python3

import sys


def week_days():
    # Assets
    number_day = ['1', '2', '3', '4', '5', '6', '7']
    
    # Introduction
    print('Welcome to the Week Day Finder')
    
    # User input
    day = input('Please, enter the day you want to check: ').lower()
    WEEK_COLOR = {'RED': '\u001b[31m', 'GREEN': '\u001b[32m'}
    
    # Day validator
    days = {
        '1' if day in number_day else 'sunday': 'Weekend Day',
        '2' if day in number_day else 'monday': 'Week Day',
        '3' if day in number_day else 'tuesday': 'Week Day',
        '4' if day in number_day else 'wednesday': 'Week Day',
        '5' if day in number_day else 'thursday': 'Week Day',
        '6' if day in number_day else 'friday': 'Week Day',
        '7' if day in number_day else 'saturday': 'Weekend Day'
    }
    
    # Logical trial
    try:
        if days[day] == 'Week Day':
            print(WEEK_COLOR['GREEN'], days[day])
            
        else:
            print(WEEK_COLOR['RED'], days[day])
            
    except KeyError:
        print('** Invalid Day **', file=sys.stderr)
        
        
def main():
    week_days()
    
    
if __name__ == '__main__':
    main()

1

使用解构赋值来玩得开心:

dictionary = {
  **dict.fromkeys(['a', 'b'], 1),
  **dict.fromkeys(['c', 'd'], 2),
  }

assert dictionary['a'] == 1
assert dictionary['b'] == 1

# Elegant!!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接