What is the purpose of `or None` in this code? - python

I've been browsing the code of pyparsing library. In there, I found the following fragment:
result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
if not result:
raise ParseException(instring, loc, self.errmsg, self)
loc = result.end()
ret = result.group()
To boil this down some more, my understanding of the result in there is:
result = firstCharacterIsCorrect(...) and self.re.match(...) or None
Here is what I don't understand: why have or None in there?
If first character is not correct, without or None we get False. If it is correct, but the regexp fails we get None from failed match anyway.
In either case (with False or with None) if not result will do the right thing.
So why add or None? What am I missing? Why is None preferable to False?

The author wants result to be None or the match object. Without or None, if the first test fails result would be False due to short-circuiting rules.

Related

Pythonic way of assigning value to a variable if statement does not raise exception

I would like to assign value to a variable if a certain statement does not raise exceptions. If exceptions occur, None should be assigned. What is the pythonic way to achieve this?
Here is a concrete example of what I'm trying to do:
try:
bar = foo(data['buzz']) # data is a dict
except KeyError:
bar = None
Points to note:
KeyError may result from buzz not existing in data
foo can still throw an exception (of any kind, depends on it's implementation) even if buzz exists in data
While your approach is readable, you might want to consider the following.
Caveat?
bar will be set to None if a KeyError occurs inside function also:
data = {'buzz': 1}
def foo(x):
abc_value = data['abc'] # this raises a `KeyError` inside function `foo`.
try:
bar = foo(data['buzz']) # data is a dict
except KeyError:
bar = None
print(bar)
# None
Solution:
Better way you can avoid this is to avoid using try-except as it's scope is nested as well:
if "buzz" in data:
bar = foo(data["buzz"])
else:
bar = None
Even more pythonically in one line:
bar = foo(data["buzz"]) if "buzz" in data else None
For completeness, another way to avoid the problem raised by Austin, with other KeyErrors in foo (not caused by "buzz" being missing from data) triggering the except clause, would be to use the else clause:
try:
val = data["buzz"]
except KeyError:
bar = None
else:
bar = foo(val)
This else clause would be executed in the event that the statements within try did not raise KeyError.
Of course, in this case you would be better explicitly testing whether the "buzz" key exists in the dictionary, as others have suggested.
I think a better way to do this in your example is:
if "buzz" in data:
bar = foo(data["buzz"])
else:
bar = None
If you're specifically asking about a dictionary, you can use myDic.get(key) instead of myDict(key), why? because get() provides a fallback value, what's a fallback ? in case key is not in myDict, use the fallback value, in your case:
bar = foo(data.get('buzz', None) # if buzz is in data, return data["buzz"] else None
bar = [f if 'buzz' in data else None for f in data][0]
The [0] part isn't as pythonic as I would like...
Assuming foo doesn't raise a KeyError:
bar = foo(data['buzz']) if 'buzz' in data else None
However, I think your approach is cleaner and more correct, based on the pythonic pattern of "ask for forgiveness rather than permission" described here: https://docs.quantifiedcode.com/python-anti-patterns/readability/asking_for_permission_instead_of_forgiveness_when_working_with_files.html

Conditional return with no else

I'm try to do something like that in Python:
return None if a is None
Instead of having:
if a is None:
return None
But I get an invalid syntax error.
I tried to use pass:
return None if a is None else pass
But it doesn't work as well. Is there a pythonian way to do it?
return None if ...
is unconditional return of a conditional value.
If you want conditional return, all what you can change is put it on single line, like that:
if a is None: return
The syntax:
c = a if condition else b
is a proxy for:
if condition:
c = a
else:
c = b
And MUST have both branches of the condition (as detailed in the grammar description from the official documentation and further discussed in the corresponding PEP), which is missing in your code. More discussion on the if/else ternary operator in Python here.
The same holds true for return:
return a if condition else b
is equivalent to:
if condition:
return a
else:
return b
In your code, if you do not have instructions after that condition, you could use:
return None if a is None else None
which turns out to be a pretty useless statement, as can be replaced with:
return None
or even just omitted altogether.
Finally:
if a is None:
return None
is already the Pythonic version of the code.
Acceptable alternatives include:
if a is None:
return
if a is None: return None
if a in None: return
PEP8 Style Guide for Python Code suggests avoiding putting multiple statements on the same line:
Compound statements (multiple statements on the same line) are generally discouraged.
however, is open to small-body one-liners:
sometimes it's okay to put an if/for/while with a small body on the same line

Cast value if it is not None in python

If you need to parse some XML which has or hasn't some entries you often end up with patterns like this:
planet = system.findall('.//planet')
row['discoveryyear'] = int(planet.findtext("./discoveryyear")) if planet.findtext("./discoveryyear") else None
Is there a nicer way to do that? I would like to avoid the second planet.findtext call but also don't want to write another line of text to store the variable first
Instead of the try/except solution, I propose a helper function:
def find_int(xml, text):
found = xml.findtext(text)
return int(found) if found else None
row['discoveryyear'] = find_int(planet, "./discoveryyear")
(note that found is also falsy if it's '', which is good case to return None for as well)
This will do (except if it's discovered in year 0 haha):
row['discoveryyear'] = int(planet.findtext("./discoveryyear") or 0) or None
To avoid the extra function call you could wrap it in a try/except
try:
row['discoveryyear'] = int(planet.findtext("./discoveryyear"))
except TypeError: #raised if planet.findtext("./discoveryyear") is None
row['discoveryyear'] = None
This also doesn't store the return value in a seperate variable

what is the meaning of this (redundant?) python expression?

str='test'
example={'test':'value',}
return str in example and example[str] or None
why the seemingly redundant extra test for key str in example?
In this specific example, the check is to first make sure that 'test' is actually a valid key in the example dict, otherwise you would get a KeyError exception. Then the logic proceeds to check the key and either return it, or a None if the value of example[str] evals to False
It would be a lot easier if this example simply did:
str='test'
example={'test':'value',}
return example.get(str, None) or None
Update
Even simpler, since the extra param to get() is not needed:
return example.get(str) or None
Update 2: Breaking down the truth tests and boolean operations from the OP (based on comments)
example = {
'test' : 'value',
'test2': 0,
'test3': [],
}
test = lambda k: k in example and example[k] or None
print test('test')
# value
print test('test2')
# None
print test('test3')
# None
For starters, the behaviour is different for the case where you're looking up a non-existent key (the extra test would prevent a KeyError exception being thrown).
However, it goes further than that because example[str] can be false in a boolean context. e.g., it could be an empty string.
>>> str='test'
>>> example={'test':[],}
>>> str in example and example[str] or None
>>> str in example or None
True
So it is not quite redundant. str in example checks for the existence of the key, whereas and example[str] is also checking the truthiness of the value.
Since Python evaluates Booleans lazily, you can safely omit parentheses in simple tests. This might make it easier to read:
(str in example and example[str]) or None
In plain English:
"Make sure the dictionary example has the key str and that the key also has a non-False value. If so, return the value of the key example[str]. Otherwise return None"
Graceful failure. If the key doesn't exist, the lookup 'example[str]' will fail at runtime. You'll get a fault, terminate your app and get a traceback. By checking for the key first you get the None value back instead and your application will go on on its merry way.
Another, more general approach would be to catch the exception and return None as a result of that.
def testfun():
str='test2'
example={'test':'value',}
try:
return example[str]
except KeyError:
return None
If the key doesn't exist it will get KeyError exception
#!/usr/bin/python
str='test'
example={'test':'value',}
if str in example and example[str]:
print example[str]
else:
print False
str in example is a boolean test to see if str is a key in example, while example[str] yields the actual value associated with that key.

Pythonic get element of array or default if it doesn't exist

We have
matches = re.findall(r'somewhat', 'somewhere')
Can we simplify this
if len(matches) > index:
return matches[index]
else:
return 'default'
or
return matches[index] if len(mathes) > index else 'default'
to something similar to JS's
return matches[index] || 'default'
that we can simply use
return 'somewhere'.match(/somewhat/)[index] || 'default'
Something like this might help:
>>> reg = re.compile('-\d+-')
>>> reg.findall('a-23-b-12-c') or ['default']
['-23-', '-12-']
>>> reg.findall('a-b-c') or ['default']
['default']
Edit
Ugly one-liner
(reg.findall('a-b-c')[index:] or ['default'])[0]
I'd be tempted to use a try except block. You'd be required to consider when index was negative though. Is this an error or an acceptable input?
But the following would work:
try:
return re.findall(r'somewhat', 'somewhere')[index]
except IndexError:
return 'default'
This is meant to be the preferred way if you're concerned about efficiency as it avoids checking the bounds of the array twice (once when you manually do it and and second when python does its internal check as well).
edit: I've never particularly liked this way as it hides any IndexErrors thrown by sub calls and would return the default (which I doubt would be the desired behaviour and a possible source of bugs).
Not really, because running mathes[x] with an invalid index will throw an IndexError, which does not return False.

Categories

Resources