Conditional return with no else - python

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

Related

python: simplify return statement (trigraph?)

Consider the following simple code:
import re
def my_match(s):
if re.match("^[a-zA-Z]+", s):
return True
else:
return False
Is there a way to collapse this in a single return statement? In C we could do for example:
return match("^[a-zA-Z]+", s) ? true : false;
Is there something similar in python?
Python also supports this, although the syntaxes is a little different than most languages.
import re
def my_match(s):
return True if re.match("^[a-zA-Z]+", s) else False
In general, the Python syntax is val_when_true if cond else val_when_false, compared to the cond ? val_when_true : val_when_false you see in other languages.
In your particular case though, you can just write:
import re
def my_match(s):
return bool(re.match("^[a-zA-Z]+", s))
A more generell solution would be to use the following code line. It excludes a fit with length 0 as it specificly checks for the None statement. In this case an empty string is impossible but it is more explicit.
return re.match("^[a-zA-Z]+", s) is not None
re.match() returns a value that can be evaluated for truthiness. So if you don't need to return the exact values True and False, you can just directly return the result of the match() function:
def my_match(s):
return re.match("^[a-zA-Z]+", s)
And then the caller can say:
if my_match(x):
...
else:
...
Although in this specific case, my_match() becomes a mostly useless wrapper function, and you could just call re.match(...) directly.
if re.match(...):
...
else:
...
The other answers show the ternary equivalent in Python. But since Python also assigns truthiness to values and expressions, you could simply use:
my_match = lambda s : bool(re.match("^[a-zA-Z]+", s))

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

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.

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

Python: avoiding if condition for this code?

for the following code
a =func()
if a != None:
b.append(a)
a can be assigned to None, is there a way to avoid the if statement and only use one line of code?
original problem is the following
import xml.etree.ElementTree as etree
r = etree.parse(f).getroot()
b = etree.Element('register',{})
a = r.find('tag_name') # a may get None if did not find it
if a != None:
b.append(a)
ok, I used all the answers and got this, personally I think it's the most complex python I have ever wrote so far, lol
NS_MAP = {
'spirit' : 'http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4',
'app' : 'http://www.app.com/SPIRIT-app'
}
mp=etree.Element('MemoryProperty', {'version':'alpha'})
mpt=etree.ElementTree(mp)
def copy_tags(tp, op, p, tn, ns='spirit'):
c = p.find('{%s}%s'%(NS_MAP[ns],tn))
if c is not None:
(op == '<-') and tp.append(c)
return c
for reg in regs:
te = etree.Element('register',{})
copy_tags(te,'<-',reg,'name')
copy_tags(te,'<-',reg,'addressOffset')
copy_tags(te,'<-',reg,'access')
(lambda e, t: copy_tags(te,'<-',t,'usageConstraints',ns='app') if t is not None else None)(te, copy_tags(te,'|',reg,'vendorExtensions'))
mp.append(te)
mpt.write('map_gen.xml')
If you can call func() beforehand, and you want to combine the test and assignment statements into a single statement, then you can do this, with an if-else expression:
b += [a] if a is not None else []
If a is not None, then this will add [a] to b -- essentially the same operation as b.append(a)
If a is None, then this will add [] to b, which will leave b unchanged.
This won't work unless b is a list, or at least supports "+=" in-place addition. If it doesn't -- perhaps it's some custom object, then you should be able to do this:
(b.append(a) if a is not None else None)
This is an expression, evaluated for its side effects, and then thrown away. If a is None, then the b.append(a) call will never be executed. In either case, the value of the expression is None, but we don't care about it, so it gets ignored.
Now, if you want to combine the func() call with this, then you'll have to do something different in order to avoid calling func twice. If you can use the "+=" syntax, then you can do it like this:
b += filter(None, [func()])
filter(None, <list>) returns the list with all false elements (None included, but also 0 and []) removed. This statement, then, will add either [func()] or [] to b.
[Edited]
Finally, for the worst case scenario: If you can't call func() more than once, and you can't use b += <list>, and you need to accept 0, "", [], etc, and only exclude None, and you need it all on one line, here's the ugliest line of code yet:
(lambda l, a: l.append(a) if a is not None else None)(b, func())
This is essentially #ekhumoro's solution, compressed into one line. It defines an anonymous function, calls it, discards the value, and then discards the function, all for the sake of the side effect.
Now, this is a single line, but it's certainly not easier to read or understand than the original code. If I were you, I'd stick with the original, or go with #ekhumoro's idea of just defining a helper function and using that.
python 3.8 walrus operator
if a := func(): b.append(a)
You asked the wrong question here. The clue is in your reply to one of the comments where you say "I have 10+ tags, if I can get 3 line to 1 line, I will save 20+ lines".
So your problem actually is not that you have 3 lines of code but that you are needlessly repeating 3 lines of code over and over. You could use a function to extract the repeated lines, but it sounds like in this case you may actually want a loop:
THE_TAGS = ('tag1', 'tag2', 'and so on')
for tag in THE_TAGS:
a = r.find(tag) # a may get None if did not find it
if a != None:
b.append(a)
Or if you need to append to different lists:
def extract_tag(r, tag_name, to):
a = r.find(tag_name) # a may get None if did not find it
if a != None:
to.append(a)
extract_tag(r, 'tag1', b)
extract_tag(r, 'tag2', c)
Short answer: Not really.
Longer answer: If you really wanted to avoid this (perhaps because you want to implement this behavior --- appending only non-None values) from several different blocks of code) then you could create a class as a proxy around the underlying b object and hide the details in its append method.
class NonNoneAppender:
def __init__(self, obj):
if not hasattr(obj, 'append') or not callable(obj.append):
raise ValueError, "Object must have append method"
self.__obj = obj
def append(self, item):
if item is not None:
return self.__obj.append(item)
def __getattr__(self, attr):
return getattr( self.__obj, attr)
... and then you could do something like:
b = NonNoneAppender(b)
However, I'm not sure this would make any sense at all for your code.
Attacking your real problem, and doing it in two lines for clarity:
temp = [r.find(tag) for tag in list_of_tags]
b.extend(x for x in temp if x is not None)
Note: Element.extend is new in Python 2.7/3.2
Presumably you're not trying to remove just a single if statement from your code...
So the obvious answer is to use a function:
import xml.etree.ElementTree as etree
def append(parent, child):
if child is not None:
parent.append(child)
r = etree.parse(f).getroot()
b = etree.Element('register',{})
append(b, r.find('tag_name'))
You can just add everything and remove Nones at the end with b = [a for a in b if b is not None]. Or, in your particular use case, you can do b.extend(r.findall('tag_name')[:1]). This may be a bit slower, however, as it will go through the whole tree, rather than stopping at the first instance.
b+=list(set([r.find('tag_name')])-set([None]))
But it's very ugly. A little cleaner, but also a line longer:
b.append(r.find('tag_name'))
b.remove(None)
Still not very neat though. If I were you I'd just keep that if statement.

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