0

I am trying to improve my understanding about dictionary list comprehension. I have created a silly list, based on what is in my driveway:

car = ['Ram', 'Ford', "Jeep", 'Jeep', 'ram']

I can create a dictionary and loop through the list

cardict = dict()
for count in car:
    count = count.upper()
    cardict[count] = cardict.get(count, 0) + 1
print(cardict)

That returns

{'RAM': 3, 'DODGE': 1, 'FORD': 1}

I have also been trying to improve my understanding of comprehension. So, I have also tried using fromkeys() and get()

For example,

again = dict.fromkeys(cardict, 0) + 1

However, I am getting a Type Error. How can I go through the dictionary and increment items in a list? I am looking for the "Pythonic" way to do this, so I would assume that there is a way without creating a for loop. Is it possible?

martineau
  • 119,623
  • 25
  • 170
  • 301
Anthony Aldea
  • 13
  • 1
  • 1
  • 6

1 Answers1

2

The "Pythonic" way is to use collections.Counter, but you'll still need a list comprehension or map to change the case:

>>> from collections import Counter
>>> car = ['Ram', 'Ford', "Jeep", 'Jeep', 'ram']
>>> Counter(car)
Counter({'Jeep': 2, 'Ram': 1, 'Ford': 1, 'ram': 1})
>>> Counter([x.upper() for x in car])
Counter({'RAM': 2, 'JEEP': 2, 'FORD': 1})
>>> Counter(map(str.upper,car))
Counter({'RAM': 2, 'JEEP': 2, 'FORD': 1})
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • 2
    Can simplify to `Counter(x.upper() for x in car)` – square brackets not needed, since `Counter` takes any iterable, including a generator expression. – FMc Sep 23 '20 at 15:58
  • @FMc while true you can eliminate the brackets, the code is faster with brackets because it doesn't have to build the list first. `timeit` shows brackets is ~7% faster for this instance. Using the brackets is a good habit, esp. for huge lists. – Mark Tolonen Sep 23 '20 at 16:01
  • Thank you for the quick response. I am trying it out myself now. I am trying to understand why you used map. As for not using the brackets @MarkTolonen I thought that comprehensions always used them? Am I understanding that incorrectly? – Anthony Aldea Sep 23 '20 at 16:11
  • @AnthonyAldea List comprehensions use brackets, generators do not. Generators generate items one-by-one as they are iterated. For discussions about list comp vs. generator, see https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehension – Mark Tolonen Sep 23 '20 at 16:15
  • @AnthonyAldea `map` is the old way of calling a function on each element of an iterable and generating a new list. `map(func,iterable)` is similar to `[func(x) for x in iterable]`. – Mark Tolonen Sep 23 '20 at 16:18
  • Thank you again. If I may ask one more question; is it possible to print the Counter without the word Counter? I see I can access the results as if it were a regular dictionary. I read that a Counter is a subtype of a dictionary. Would I just want to convert the Counter to a standard dictionary? I know I can convert it by using dict(), but does it matter in the long run? I appreciate all of the help. – Anthony Aldea Sep 23 '20 at 16:28
  • @MarkTolonen Sure, I don't dispute the benchmarks – but I rarely care, because most code is not used in a context where performance optimizations matter. The better habit, IMO, is to prioritize readability/simplicity, and then make adjustment when raw speed is the top goal. In any case, your explanation for the benchmark is puzzling: "the code is faster with brackets because it doesn't have to build the list first". But the square brackets do build a list first, right? Am I misunderstanding some detail, or is your explanation off? – FMc Sep 23 '20 at 16:29
  • @FMc It's probably off :^) Generators are slower but more memory efficient. Basically, someone always comments. If I leave the brackets off, someone comments that comprehensions are faster. If I leave them in, someone comments that they aren't required. Can't win :^P It's worth understanding that list comprehensions are more performant when it matters. I've made the same comment as you did in the past, and people argue that use the brackets all the time in these cases and you'll always be performant. People just like to argue. – Mark Tolonen Sep 23 '20 at 16:36
  • 1
    @FMc [This is the answer](https://stackoverflow.com/a/9061024/235698) I was thinking of about building the list first. `.join()` makes two passes over the data which is inefficient with a generator. So it is more likely the overhead of the call frames with yield that makes generator a bit slower for Counter, which probably only does one pass. – Mark Tolonen Sep 23 '20 at 16:53
  • @MarkTolonen That's an interesting link. I've been tripping over slightly counter-intuitive benchmarking situations in Python lately. Apologies for playing the troll in this conversation! I should go do something constructive for a change. – FMc Sep 23 '20 at 17:17