4

I have a dictionary which may or may not have one or both keys 'foo' and 'bar'. Depending on whether both or either are available, I need to do different things. Here is what I am doing (and it works):

foo = None bar = None if 'foo' in data: if data['foo']: foo = data['foo'] if 'bar' in data: if data['bar']: bar = data['bar'] if foo is not None and bar is not None: dofoobar() elif foo is not None: dofoo() elif bar is not None: dobar() 

This seems too verbose - what is the idiomatic way to do this in Python (2.7.10)?

4
  • you can do foo = data.get('foo') Commented Sep 19, 2017 at 16:46
  • 1
    @EricDuminil No, dofoobar() is different from dofoo() followed by dobar() Commented Sep 19, 2017 at 17:04
  • Wait, is it a typo with bar and email or do you really check two different keys? Commented Sep 19, 2017 at 17:11
  • @EricDuminil Was a typo. Fixed now. Sorry. Commented Sep 19, 2017 at 17:18

4 Answers 4

6

You can use dict.get() to shorten your code. Rather than raising a KeyError when a key is not present, None is returned:

foo = data.get('foo') bar = data.get('email') if foo is not None and bar is not None: dofoobar() elif foo is not None: dofoo() elif bar is not None: dobar() 
Sign up to request clarification or add additional context in comments.

6 Comments

An idiomatic alternative for the first if clause could be all([foo, bar]) if all that matters is truthyness, not exact comparison with None
@LukasGraf Well, I'm not exactly sure about that :| Wouldn't just doing if foo and bar be faster and cleaner if all that matters is truthyness?
@LukasGraf I'm not sure this was the intention, but in the OP's code all that matters is truthiness, because if data['foo'] doesn't evaluate to true, foo will never get a value other than None.
@ChristianDean that's true, that should be functionally equivalent and even cleaner. I don't think it would be significantly fast, as all() also short-circuits (returns on the first non-True element).
@yinnonsanders yeah, I noticed that as well. However, Christian's answer does demonstrate nicely that if you want to make that distinction, you compare the values to None (and do so using is instead of ==), which is why I like it.
|
4

Here's another way to refactor your code :

foo = data.get('foo') bar = data.get('bar') if foo: if bar: dofoobar() else: dofoo() elif bar: dobar() 

I'm not sure it's cleaner or more readable than ChristianDean's answer, though.

Just for fun, you could also use a dict with boolean tuples as keys and functions as values:

{(True, True):dofoobar, (False, True):dobar, (True, False):dofoo} 

You'd use it this way:

data = {'foo': 'something'} foo = data.get('foo') bar = data.get('bar') def dofoobar(): print('foobar!') def dobar(): print('bar!') def dofoo(): print('foo!') actions = {(True, True):dofoobar, (False, True):dobar, (True, False):dofoo} action = actions.get((foo is not None, bar is not None)) if action: action() #foo! 

1 Comment

I like the first approach better than other answers (though the second one is more fun :-)) - actually, (in @ChristianDean's answer) I think checking for not None after data.get() does not validate that the strings are non-empty.
0
>>> data = {'foo': 1} >>> foo = data.get('foo') >>> foo 1 >>> bar = data.get('bar') >>> bar >>> bar is None True 

Comments

-1
foo = None bar = None try: foo=data["foo"] except: pass try: bar=data["bar"] except: pass if foo and bar: dofoobar() elif foo: dofoo() elif bar: dobar() 

You can use try/except.Also get attribute is also perfect

2 Comments

You are right. EAFP is the commonly preferred idiom in Python. However, dict.get already implements the logic for you, so using it here just makes the code more verbose.
ya I thought providing an alternative would also be fine

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.