6 ms·
A more sane* way to implement the single lookup behavior is: def values_in(keys, d): ret = {} for key in keys: try: ret[k
by typomatic 12y ago
A more sane* way to implement the single lookup behavior is:
def values_in(keys, d):
ret = {}
for key in keys:
try:
ret[key] = d[key]
except KeyError:
pass
return ret
which works much more quickly in cases where keys is nearly all of d.keys, and doesn't force you to reinvent monads in Python, which is arguably unsuited for them.
* Sanity values may vary for people who haven't gotten into the habit of using Python exceptions for little things.
- dragonwriter 12y agoThe article is looking at returning a list, while your option returns a dict, but that's an easy fix. And even for an Option-like approach, the article tries way too hard: Option can be viewed as a constrained cardinality list, and regular lists work just fine to implement them. from itertools import chain def getOpt(d, k): return [d[key]] if key in d else [] def values_in(keys, d): return list(chain(*[ getOpt(d,key) for key in keys ])) Or Ruby: def values_in(keys, d) keys.flat_map {|k| d.has_key?(k) ? [d[k]] : []} end
- rntz 12y agoAuthor of the article here. The Python solution using lists-as-options is actually even simpler: def values_in(keys, d): return [x for key in keys for x in getOpt(d,key)] Using lists as options is a neat trick I hadn't thought of when writing the article. Unfortunately, no dynamic language uses this idiom (or really any other non-exception option-like patterns) natively.
- dragonwriter 12y ago> The Python solution using lists-as-options is actually even simpler: Good point. For some reason, I have this giant python blind spot for comprehensions where the sources aren't orthogonal -- I miss them in the first pass all the time; I normally notice it fairly quickly in code I'm not posting on a discussion forum... > Unfortunately, no dynamic language uses this idiom (or really any other non-exception option-like patterns) natively. Yeah, to use it in your own code in most popular dynamic languages, you have to wrap library code that works differently. The good thing is that most popular dynamic libraries have good (largely functionally-inspired) tools for working with lists and other iterables that the payoff is pretty good. (Of course, with duck typing in most popular dynamic languages, one thing you could do to be more explicit is to combine the Option-as-class and Option-as-list approach, and make Option a class that implemented the interface used by immutable collections.)
- chriswarbo 12y agoActually, at least in PHP, I find myself using arrays and other iterables as pseudo-Options quite a lot; eg. when checking the existence of a DB value, xpath element, etc. // Using if/then/else function foo($x) { $results = look_up_something($x); if (count($results) > 0) { $val = $results[0]; return do_something($val); } } // Same as above, but with a foreach loop function foo($x) { foreach (look_up_something($x) as $val) { return do_something($val); } } Of course, the nice thing about Options (other than being able to distinguish between "None", "Some None", "Some (Some None)", etc.) is the availability of combinators to lift and compose non-Option functions with optional functions. Sometimes we can map and fold ("reduce"), but sometimes we can't (eg. PHP's "array_map" doesn't work on iterable objects, which many APIs use in lieu of arrays).
- wylee 12y agoThat version (and the one in the article) are somewhat confusing to parse and detract from the point of the article. This seems more "Pythonic": def lookup(d, key): return Just(d[key]) if key in d else None def values_in(keys, d): values = [] for k in keys: x = lookup(d, k) if x: values.append(x.value) return values If you really want a one-liner, I think this is easier to understand: def values_in(keys, d): return [x.value for x in (lookup(d, k) for k in keys) if x]
- falcolas 12y agoWell, considering that this is an indirection on top of the `in` operator which will slow the whole thing down, of course they're not typically implemented in idiomatic solutions in Python. The obvious being said, you can also use functions and lambdas to get around having to create custom classes or abuse lists. def value(x): return lambda: x def values_in(keys, d): return [x() for x in (value(d[key]) if key in d else None for key in keys) if x] def get_maybe(d, k): return value(d[k]) if k in d else None def values_maybe(keys, d): """ If you want the caller to be responsible for handling the maybe case """ return [get_maybe(d, k) for k in keys]
- james2vegas 12y agoin Perl: sub values_in(\@\%) { my ($keys, $d) = @_; grep { $d->{$_} } @$keys }
- anon4 12y agoAs long as we're playing code golf, try this for python: def values_in(dict, keys): marker = object() return [x for x in (dict.get(k, marker) for k in keys) if x is not marker]
- dragonwriter 12y agoFor this case and similar cases where the API provides a "supply your own default value for the failure case" option, this kind of "unique one-time null marker" is actually a very good approach that addresses much of the problem with standard null values. Its not as composable as Option- or list-based approaches, though. (OTOH, you can use it to implement those approaches, and its cleaner for that than the way I did that elsewhere in the thread.)