def isBig(x):
if x > 4:
return \'apple\'
else:
return \'orange\'
This works:
if isBig(y): return isBig(y)
The one liner doesn't work because, in Python, assignment (fruit = isBig(y)
) is a statement, not an expression. In C, C++, Perl, and countless other languages it is an expression, and you can put it in an if
or a while
or whatever you like, but not in Python, because the creators of Python thought that this was too easily misused (or abused) to write "clever" code (like you're trying to).
Also, your example is rather silly. isBig()
will always evaluate to true
, since the only string that's false is the empty string (""
), so your if
statement is useless in this case. I assume that's just a simplification of what you're trying to do. Just do this:
tmp = isBig(y)
if tmp: return tmp
Is it really that much worse?
Starting Python 3.8
, and the introduction of assignment expressions (PEP 572) (:=
operator), it's now possible to capture the condition value (isBig(y)
) as a variable (x
) in order to re-use it within the body of the condition:
if x := isBig(y): return x
The problem is that the assignment operation cannot be evaluated as having a boolean value. The if
statement relies on being able to evaluate a boolean. For example,
>>> fruit = 'apple'
>>> bool(fruit = 'apple')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/jem/<ipython console> in <module>()
TypeError: 'fruit' is an invalid keyword argument for this function
>>> bool('a')
True
This doesn't work due to intentional language design, but you can use this trick to get around this decision
print "apple" if x > 4 else "orange"
I see somebody else has already pointed to my old "assign and set" Cookbook recipe, which boils down in its simplest version to:
class Holder(object):
def set(self, value):
self.value = value
return value
def get(self):
return self.value
h = Holder()
...
if h.set(isBig(y)): return h.get()
However, this was intended mostly to ease transliteration between Python and languages where assignment is directly supported in if
or while
. If you have "hundreds" of such check-and-return in a cascade, it's much better to do something completely different:
hundreds = isBig, isSmall, isJuicy, isBlah, ...
for predicate in hundreds:
result = predicate(y)
if result: return result
or even something like
return next(x for x in (f(y) for f in hundreds) if x)
if it's OK to get a StopIteration exception if no predicate is satisfied, or
return next((x for x in (f(y) for f in hundreds) if x)), None)
if None
is the proper return value when no predicate is satisfied, etc.
Almost invariably, using (or even wishing for;-) the Holder
trick/non-idiom is a "design smell" which suggests looking for a different and more Pythonic approach -- the one case where Holder
is justified is exactly the special case for which I designed it, i.e., the case where you want to keep close correspondence between the Python code and some non-Python (you're transliterating a reference algorithm in Python and want it working first before refactoring it into a more Pythonic form, or you're writing Python as a prototype that will be transliterated into C++, C#, Java, etc, once it's working effectively).