Assuming connectionDetails is a Python dictionary, what’s the best, most elegant, most “pythonic” way of refactoring code like this?
if "host" in connectionDetails:
host = connectionDetails["host"]
else:
host = someDefaultValue
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Like this:
host = connectionDetails.get('host', someDefaultValue)
Method 2
You can also use the defaultdict like so:
from collections import defaultdict a = defaultdict(lambda: "default", key="some_value") a["blabla"] => "default" a["key"] => "some_value"
You can pass any ordinary function instead of lambda:
from collections import defaultdict def a(): return 4 b = defaultdict(a, key="some_value") b['absent'] => 4 b['key'] => "some_value"
Method 3
While .get() is a nice idiom, it’s slower than if/else (and slower than try/except if presence of the key in the dictionary can be expected most of the time):
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="try:n a=d[1]nexcept KeyError:n a=10")
0.07691968797894333
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="try:n a=d[2]nexcept KeyError:n a=10")
0.4583777282275605
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="a=d.get(1, 10)")
0.17784020746671558
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="a=d.get(2, 10)")
0.17952161730158878
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="if 1 in d:n a=d[1]nelse:n a=10")
0.10071221458065338
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
... stmt="if 2 in d:n a=d[2]nelse:n a=10")
0.06966537335119938
Method 4
For multiple different defaults try this:
connectionDetails = { "host": "www.example.com" }
defaults = { "host": "127.0.0.1", "port": 8080 }
completeDetails = {}
completeDetails.update(defaults)
completeDetails.update(connectionDetails)
completeDetails["host"] # ==> "www.example.com"
completeDetails["port"] # ==> 8080
Method 5
This is not exactly the question asked for but there is a method in python dictionaries: dict.setdefault
host = connectionDetails.setdefault('host',someDefaultValue)
However this method sets the value of connectionDetails['host'] to someDefaultValue if key host is not already defined, unlike what the question asked.
Method 6
(this is a late answer)
An alternative is to subclass the dict class and implement the __missing__() method, like this:
class ConnectionDetails(dict):
def __missing__(self, key):
if key == 'host':
return "localhost"
raise KeyError(key)
Examples:
>>> connection_details = ConnectionDetails(port=80) >>> connection_details['host'] 'localhost' >>> connection_details['port'] 80 >>> connection_details['password'] Traceback (most recent call last): File "python", line 1, in <module> File "python", line 6, in __missing__ KeyError: 'password'
Method 7
Testing @Tim Pietzcker’s suspicion about the situation in PyPy (5.2.0-alpha0) for Python 3.3.5, I find that indeed both .get() and the if/else way perform similar. Actually it seems that in the if/else case there is even only a single lookup if the condition and the assignment involve the same key (compare with the last case where there is two lookups).
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:n a=d[1]nexcept KeyError:n a=10")
0.011889292989508249
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:n a=d[2]nexcept KeyError:n a=10")
0.07310474599944428
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(1, 10)")
0.010391917996457778
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(2, 10)")
0.009348208011942916
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 1 in d:n a=d[1]nelse:n a=10")
0.011475925013655797
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:n a=d[2]nelse:n a=10")
0.009605801998986863
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:n a=d[2]nelse:n a=d[1]")
0.017342638995614834
Method 8
You can use a lamba function for this as a one-liner. Make a new object connectionDetails2 which is accessed like a function…
connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"
Now use
connectionDetails2(k)
instead of
connectionDetails[k]
which returns the dictionary value if k is in the keys, otherwise it returns "DEFAULT"
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0