3 ms·
Here's a little "default dict puzzle" for you. A common use case for default dict is counting the number of occurrences of some string in an input file/stream
by jbotz 5y ago
Here's a little "default dict puzzle" for you. A common use case for default dict is counting the number of occurrences of some string in an input file/stream by providing a default of zero and just incrementing the value for every key you see. But what if you want to use it to count the order in which we see keys, i.e. the first failed lookup initilizes to 1, the second to 2, etc? There are a number of solutions to this, I found 5 that are a few lines each, although a couple of these can be shoehorned into one-liners.
- BugsJustFindMe 5y ago> The most common use case for default dict is counting the number of occurrences of some string I don't see how you can declare what the most common use of a standard function is, so, uh, citation needed. > But what if you want to use it to count the order in which we see keys Don't bother. Since python 3.7 (really 3.6) you can just look at the key order after adding your strings to a normal dict comprehension because dicts are ordered now. {s: 0 for s in strings}.keys() will give you the order. If you really want a number associated with each, you can wrap that in enumerate.
- jbotz 5y agoOK, a very common use case. As for your second part, yes, that would make my puzzle obsolete, although it's still an interesting exercise in the different ways you can keep state in Python.
- remram 5y agoWhy wouldn't you use Counter for that? https://docs.python.org/3/library/collections.html#collections.Counter https://docs.python.org/3/library/collections.html#collectio...
- BugsJustFindMe 5y ago> Why wouldn't you use Counter for that? Counters are for counting, not for determining order of discovery.
- dharmab 5y ago> Changed in version 3.7: As a dict subclass, Counter Inherited the capability to remember insertion order. Math operations on Counter objects also preserve order. Results are ordered according to when an element is first encountered in the left operand and then by the order encountered in the right operand.
- BugsJustFindMe 5y ago> Changed in version 3.7: As a dict subclass, Counter Inherited the capability to remember insertion order. Yes but the question as framed ("first failed lookup initilizes to 1, the second to 2, etc") does not require counting the occurrences anymore, so using a counter does more work than necessary. If you want both counts _and_ order, then, yeah, counter is great.
- remram 5y agoI was replying to this: > The most common use case for default dict is counting the number of occurrences of some string I would assume very few people use defaultdict for this since Counter exists.
- BugsJustFindMe 5y agoOh, yeah, that's legit. Secretly (not secretly anymore, I guess!) I assume that the OP comment was a school homework assignment or something.
- fragmede 5y agoDespite attempts to depreciate Python 2, there's still a lot of code that hasn't been updated to use all of the new language features since Python 3.1, so it's anybody's guess, really. Depends on how much legacy code you interact with.
- coldtea 5y ago>I don't see how you can declare what the most common use of a standard function is, so, uh, citation needed. In a casual discussion you don't formally "declare", you just state something based on your observations and the patterns you see in the wild, that might or might not be true in general. Similarly, if it's a casual discussion, one can just read "the most common use case" as "a common use case", and just move on to the substance of the parent comment.
- scienceman 5y agoMost "straightforward" way I could think of: ``` import itertools, collections cnt = itertools.count(1) d = collections.defaultdict(lambda: next(cnt)) s = "abcad" [d[c] for c in s] ```
- klyrs 5y agoOh, this is nice. You can do it in one line (plus imports) with d = defaultdict(count().__next__)
- scienceman 5y agoInteresting... I wonder if this is vulnerable to getting garbaged collected at any point during execution.
- klyrs 5y agoNo, the __next__ slot wrapper holds a reference to its associated count object.
- klyrs 5y agoOne horribly fragile solution to this depends on global name resolution: d = defaultdict(lambda: len(d)) However, it's probably better to avoid defaultdict altogether and implement __missing__ in a subclass of dict.