100

In Python, I need a dictionary object which looks like:

{'a': 10, 'b': 20, 'c': 10, 'd': 10, 'e': 20}

I've been able to get this successfully by combining the dict.update() and dict.fromkeys() functions like so:

myDict = {}
myDict.update(dict.fromkeys(['a', 'c', 'd'], 10))
myDict.update(dict.fromkeys(['b', 'e'], 20))

However, because the code is being written for novice users who may need to make add keys/values on occasion, I'd prefer a simple bare-bones (Perl-like) syntax such as:

myDict = {}
myDict['a', 'c', 'd'] = 10
myDict['b', 'e'] = 20

This, however, gives me:

myDict = {('a', 'c', 'd'): 10, ('b', 'e'): 20}

Is there a way I can simplify my first example (using dict.update() and dict.fromkeys()) further, and get the dict object I'm looking for?

Or, alternatively, if I have a dict with tuples as in my second example, is there an easy way for me to do a lookup such as myDict['c'] or myDict.get('c') and get the value 10?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
BillyBBone
  • 3,144
  • 3
  • 23
  • 27

8 Answers8

116

I would say what you have is very simple, you could slightly improve it to be:

my_dict = dict.fromkeys(['a', 'c', 'd'], 10)
my_dict.update(dict.fromkeys(['b', 'e'], 20))

If your keys are tuple you could do:

>>> my_dict = {('a', 'c', 'd'): 10, ('b', 'e'): 20}
>>> next(v for k, v in my_dict.items() if 'c' in k)      # use .iteritems() python-2.x
10

This is, of course, will return first encountered value, key for which contains given element.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 9
    The OP said the code is being written for novices, and this is relatively difficult to comprehend for a novice. – taleinat Jun 04 '10 at 12:41
  • 34
    @teleinat: so write a function that they could use. Are you seriously downovoting me because my code is not stupid enough? My code is using built-in methods and function. How is that not basics? What is the point if writing code for novice if they're not going to learn from it? – SilentGhost Jun 04 '10 at 12:42
  • 7
    The OP obviously knows how to program in Python. I interpret his question as whether there is a simpler built-in solution for his use-case, not whether it is possible to write such a function or achieve what he wanted with complex code. Therefore, as I understand it SilentGhost's response doesn't answer the question, so I downvoted it. My initial comment didn't describe my reason for downvoting. – taleinat Jun 04 '10 at 13:30
  • @taleinat: *Or, alternatively, if I have a dict with tuples as in my second example, is there an easy way for me to do a lookup such as `myDict['c']` or `myDict.get('c')` and get the value `10`?* are we not reading the same question? Proposed solution is the only *easy* way to do this. – SilentGhost Jun 04 '10 at 13:33
  • 6
    @SilentGhost: IMO the OP knows how to program and could have conjured up something equivalent himself; he was asking if there was an existing, simple mechanism for this, e.g. built-in or in the standard library. – taleinat Jun 04 '10 at 13:36
  • @taleinat: surely if OP *knows how to program*, he knows that there isn't anything built-in of sorts. – SilentGhost Jun 04 '10 at 13:37
  • 8
    @SilentGhost I disagree. I've been programming in Python for nine years and still once in a while I learn of new functionality in the standard library. Knowing to use a language and knowing its entire standard library inside and out are very different things. I don't think either of us is going to be conviced, however, so let's just agree to disagree. – taleinat Jun 04 '10 at 13:45
  • 1
    taleinat & SilenGhost, thank you for putting some thought into my question. Discussion is good! – BillyBBone Jun 10 '10 at 13:04
  • `my_dict = {frozenset({'a', 'c', 'd'}): 10, frozenset({'b', 'e'}): 20}` for O(1) lookup in keys – abdelgha4 May 25 '22 at 18:15
66

Similar to @SilentGhost but a more declarative syntax (with Python 3.5+) I prefer:

myDict = {
  **dict.fromkeys(['a', 'c', 'd'], 10), 
  **dict.fromkeys(['b', 'e'], 20)
}
wjandrea
  • 28,235
  • 9
  • 60
  • 81
shao.lo
  • 4,387
  • 2
  • 33
  • 47
  • 4
    The tuple method doesn't work for myDict.get('c'), which is what the question was about. This method does work as expected, without adding code to access or define the dictionary. This should be the checked answer. – rickfoosusa Sep 30 '17 at 00:08
  • This is the cleanest way – Swapnil Masurekar Jul 08 '21 at 04:46
  • What is the ** notation? – PingPong Nov 19 '22 at 01:51
  • @PingPong ** is unpacking. myDict is created by getting the data out of the two dictionaries that are created with the fromkeys calls. – shao.lo Nov 19 '22 at 20:03
  • I was attempting to implement this method as part of a dict-comprehension and discovered that this results in a syntax error: "SyntaxError: iterable unpacking cannot be used in comprehension". Is there a method of implementing this so that dict-comprehension may be used? – Gerald Dec 30 '22 at 20:48
14

Your first example can be simplified using a loop:

myDict = {}
for key in ['a', 'c', 'd']:
    myDict[key] = 10
for key in ['b', 'e']:
    myDict[key] = 20

No specialized syntax or trickery, and I can't think of anything which would be easier to understand.

Regarding your second question, there is no simple and efficient way to do the lookup as in your second example. I can only think of iterating over the keys (tuples) and checking whether the key is in any of them, which isn't what you're looking for. Stick to using a straightforward dict with the keys you want.

In general, if you are aiming for code that can be understood by novices, stick to the basics such as if conditions and for/while loops.

taleinat
  • 8,441
  • 1
  • 30
  • 44
12

Dict union (3.9+)

Now with Python 3.9, you can do this:

myDict = dict.fromkeys(['a', 'c', 'd'], 10) | dict.fromkeys(['b', 'e'], 20)

Although personally, I'm not sure I would use this, since it's hard to read.

Dict comprehension

myDict = {
    k: v
    for keys, v in [(['a', 'c', 'd'], 10), (['b', 'e'], 20)]
    for k in keys
    }

This is also hard to read, but I'm mentioning it for the sake of completeness.

reference

wjandrea
  • 28,235
  • 9
  • 60
  • 81
7

You could inherit from dict to implement a sort of "update from keys":

class LazyDict(dict):
    def keylist(self, keys, value):
        for key in keys:
            self[key] = value

>>> d = LazyDict()
>>> d.keylist(('a', 'b', 'c'), 10)
>>> d
{'a': 10, 'c': 10, 'b': 10}

but I prefer loop solution

wjandrea
  • 28,235
  • 9
  • 60
  • 81
remosu
  • 5,039
  • 1
  • 23
  • 16
4

Method:

def multi_key_dict_get(d, k):
    for keys, v in d.items():
        if k in keys:
            return v
    return None

Usage:

my_dict = {
   ('a', 'b', 'c'): 10,
   ('p', 'q', 'r'): 50
}

value = multi_key_dict_get(my_dict, 'a')
Shameem
  • 2,664
  • 17
  • 21
1

While @SilentGhost's answer works pretty fine with single length of keys, it won't work correctly for those looking for a "multiple character length of keys" solution, and so I've thought of the below solution [...]

let's say that we have the above dict and keys we are looking for:

my_dict = {
    'key1':'KEY_1',
    ('tk1', 'tk2','tk3'):'TK_1_2_3',
    'key2':'KEY_2'
}
my_keys = ['key2','ke', 'tk2','k','key','exception'] # key's I'm looking for

the example & SOLUTION below:

for key in my_keys:
    print(next((v for k, v in my_dict.items() if (key == k) or (isinstance(k,tuple) and key in k)),None))

CORRECTLY outputs:

KEY_2
None
TK_1_2_3
None
None
None

While with (a slightly modified solution [so it won't throw StopIteration exception] of) @SilentGhost's answer

for key in my_keys:
    print(next((v for k, v in my_dict.items() if key in k),None)) # added ((...),None)

the results are WRONG because [...]2 if not a StopIteration exception:

KEY_2
KEY_1
TK_1_2_3
KEY_1
KEY_1
None

While personally I wouldn't really recommend it from a perspective of speed efficiency (at least not for all use cases), it is indeed a way of solving this issue and so I decided to post it.

Giorgos Xou
  • 1,461
  • 1
  • 13
  • 32
0
class MyDict(dict):
    def __setitem__(self,keys,value):
        if type(keys)!=tuple:keys=(keys,)
        for key in keys:super().__setitem__(key,value)

myDict = MyDict()
myDict['a', 'c', 'd'] = 10
myDict['b', 'e'] = 20

print(myDict) # {'a': 10, 'c': 10, 'd': 10, 'b': 20, 'e': 20}
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 31 '22 at 14:52