How to assign a variable in an IF condition, and then return it?

Question:

def isBig(x):
   if x > 4: 
       return 'apple'
   else: 
       return 'orange'

This works:

if isBig(y): return isBig(y)

This does NOT work:

if fruit = isBig(y): return fruit

Why doesn’t the 2nd one work!? I want a 1-liner. Except, the 1st one will call the function TWICE.

How to make it 1 liner, without calling the function twice?

Asked By: TIMEX

||

Answers:

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
Answered By: Peter

If you want to code in PHP (or C), code in it. Don’t try to force its methods onto another language.

One of the basic tenets behind Python (in my opinion) is its readability. You should be using:

fruit = isBig(y)
if fruit: return fruit

I should also mention that your use of isXXX() is very strange; it’s usually used to return boolean values. Especially in this case where you’re using it in an IF statement.

Answered By: paxdiablo

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?

Answered By: Chris Lutz

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).

Answered By: Alex Martelli

You could use a generator:

def ensure(x):
    if x: yield x

for fruit in ensure(isBig(y)):
    return fruit
Answered By: max

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
Answered By: Xavier Guihot

How to solve a similar problem using one line loop? For example:

[m.group(1) for item in lst if m:=re.match('.*prices+(d+)', item)]
Answered By: stansy