0
foods={}
name="potato"
foods[name]=foods.get(name, 0) +1
print(foods)

I'm creating a dictionary and a string. Then, next line, everything goes insane...

Suddenly it looks like we are supposing that the new string already exists in the new dictionary...

foods[name]=

As I understand from comments here, we're getting the Dictionary's key "name" with value 0,

foods.get(name, 0)

and adding one to the key's value.

+1

How can I reason this so it makes sense?

Related:

How can I add new keys to a dictionary?

Why dict.get(key) instead of dict[key]?

chrips
  • 4,996
  • 5
  • 24
  • 48
  • 3
    `usernames={}` is _not_ an array - it's a dictionary, and this is exactly why you can "suppose that this string already exists". Also, `usernames.get(name, 0)` is _not_ "getting the string AT ZERO" - [it's attempting to retrieve the value of this key and returning zero if there's no such key](https://docs.python.org/3.8/library/stdtypes.html#dict.get). – ForceBru Oct 12 '20 at 15:13
  • @RocketHazmat The original code was from Coursera – chrips Oct 12 '20 at 15:16
  • 1
    If name in dic value is value + 1, if not 0+1 – Sergey Bushmanov Oct 12 '20 at 15:17
  • I'm talking about the syntax, and how I can read it so that it makes sense. Its not nice syntax is it? Suddenly on line 3, this syntax is saying "The Dictionary key called "name"... which doesnt exist yet – chrips Oct 12 '20 at 15:20
  • 1
    If you're so confused, didn't you consider looking at [the official docs](https://docs.python.org/3/library/stdtypes.html#dict.get)? Obviously you are not familiar with the `get` method, I'm sure it would answer most of your confusion... – Tomerikoo Oct 12 '20 at 15:21
  • @Tomerikoo here we go again. No the offical docs don't make this any better. " get(key[, default]) Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError." ---> this doesnt explain my question at all... At all. I'm talking about the first part of this sentence "foods[names]". – chrips Oct 12 '20 at 15:22
  • 2
    Related: [Python dictionary increment](https://stackoverflow.com/q/12992165/4518341), [Why dict.get(key) instead of dict\[key\]?](https://stackoverflow.com/q/11041405/4518341) – wjandrea Oct 12 '20 at 15:22
  • 1
    How come? You thought that the `0` is indexing some string. Looking at the docs will make it clear that it is the default value in case `name` is not in the dict and not an index... – Tomerikoo Oct 12 '20 at 15:22
  • @wjandrea Perfect, that helps, thank you. – chrips Oct 12 '20 at 15:24
  • Yes I read your question very carefully. You didn't mention specifically what part confuses you more than others... And `food[name] = ...` is a dict assignment... It **NEVER** assumes the key exists already... – Tomerikoo Oct 12 '20 at 15:25
  • @Tomerikoo But the syntax is written as if it exists... foods[name]= – chrips Oct 12 '20 at 15:26
  • 1
    But that's how you assign a new `key:value` pair to a dict. Notice that it's different from `x = food[names]` this indeed assumes explicitly that `name` is in `foods` and would also raise an error if it's not! But doing `food[names] = ` will always work as you are assigning a new value to a key. The main difference is that `food[name] = ...` invokes the `__setitem__` method of the dict while `x = food[name]` invokes the `__getitem__` method! Hope that makes it clear :) – Tomerikoo Oct 12 '20 at 15:29
  • 1
    And as to your question, you can see that no one assumed this is the part that confuses you because that's just how dict assignment works. This is why most people would assume that your problem is with the `foods.get(name, 0) +1` part. Next time, try to be more clear and focused on your question – Tomerikoo Oct 12 '20 at 15:31
  • @Tomerikoo Ok thanks. It's just a bit bizarre to add an element to a dictionary this way, during it's instatniation isn't it? "That's the way it is pal" I know... but this is in a Coursera course and I guess I wish it had been more clear. – chrips Oct 12 '20 at 15:35
  • 1
    What do you mean? You instantiated it as an empty dict. Now you can add/change values of keys. Related: https://stackoverflow.com/questions/1024847/how-can-i-add-new-keys-to-a-dictionary – Tomerikoo Oct 12 '20 at 15:35
  • @Tomerikoo Oh yeah. I guess I've never seen a dictionary being handled this way before. Your answer really fleshed it out! – chrips Oct 12 '20 at 15:57

2 Answers2

2

The dict.get() method takes 2 arguments: The key and a default value if that key doesn't already exist in the dict.

So foods.get(name, 0) means look up the name key in foods and return its value, otherwise return 0.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
2

Well I believe the first lines are obvious:

foods = {}
name = "potato"

The important thing to remember here is that dicts are mutuable objects.


Next, what we have is a dict assignment:

foods[name] = x

As described in How can I add new keys to a dictionary?, this basically says: "Associate the value x to the key name inside foods". If name already exists in foods with some value, that value will be overridden with x. If it does not exist in the dict, a new key:value pair will be created.

This will NEVER raise an error*, because we don't assume anything about the keys - it is not necessary. As explained above, if the key doesn't exist a new pair will be created. If the key exists already, it will be updated. Internally, the dict's __setitem__ method is called to allow this behaviour: foods[name] = x --> foods.__setitem__(name, x).


The value in this case is:

foods.get(name, 0) + 1

As described in Why dict.get(key) instead of dict[key]?, The get method is used if we don't want the code to fail if some keys are missing. It has an optional argument default (which its default is None). This means that dict.get(key, default) works the same as dict[key] as long as the key exists in the dict! If it doesn't exist already, then default will be returned instead.

So this line can be read as "add 1 to the value of name from foods if it exists in the dict, else just return 1 (0 + 1)". This is a common construct for implementing some counter. We can use this single line to replace the following cumbersome structure:

if name in foods:
    foods[name] += 1
else:
    foods[name] = 1

You can now see the direct replacement: If the key exists, get will return its value, then we add 1 and save it back to the dict - equivalent to foods[name] += 1. If it doesn't exist in the dict, get will return 0, then we add 1 to get 1, and save that in the dict - equivalent to foods[name] = 1.


Another alternative is to use a defaultdict with int type:

foods = defaultdict(int)
foods[name] += 1

This encapsulates all that logic and when a key doesn't exist in the dict and we try to access it, the type we provided is called to create a default value. In this case, int() returns 0 as desired.


* For hashable keys.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61