1

How are you supposed to write nested if statements in lambda functions?

I'm trying to use a filter function using lambda:

final_list = filter(lambda x: False if isinstance x(float) or isinstance(x, int) if x<5, another_list)

What I'm trying to do is:

  1. Check if the value is float or int
  2. If float or int, check if < 5
  3. If < 5, return False so filter will move on to next element of list.

My error says that my nested if x < 5 is invalid syntax.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Josh
  • 19
  • 3
  • So what should be returned if the value is **not** a float or int, or is 5 or greater? – Martijn Pieters Jul 18 '16 at 08:31
  • 4
    In this case I would just use a regular (`def`) function, makes it much easier to read, test and understand. – jonrsharpe Jul 18 '16 at 08:31
  • @TigerHawk: this isn't actually anything that needs a conditional expression. – Martijn Pieters Jul 18 '16 at 08:33
  • You don't even need another `if`: Python has short-circuiting. `lambda x: not (isinstance x(float) or isinstance(x, int) and x<5)`. If you have something more complicated than that, that can't just return its own truth value, use a ternary. – TigerhawkT3 Jul 18 '16 at 08:33
  • @MartijnPieters, Sorry. Maybe this will clear things up: 1) If not a float or int, print the value, 2) if float or int, and is greater than 5, print the value. I think I misunderstood how filter fundamentally works and was trying to write it so that it would forcibly remove values that don't fit the criteria even though it automatically does. The way you answered below helped me clear up the bug so that it works now. – Josh Jul 18 '16 at 08:40

2 Answers2

4

You can't use statements in a lambda, only expressions. In addition, your isinstance() calls are wrong too.

In this case, you only need a boolean expression anyway:

final_list = filter(lambda x: not (isinstance(x, (int, float)) and x < 5), another_list)

This'll only return False if both tests are true, so x is a number (integer or float) and is smaller than 5. True is returned otherwise. That means that any value that is not an int or float, or is 5 or greater, is included in the output.

If you invert the numeric test (return true for numbers 5 or over), the test becomes a little more readable:

final_list = filter(lambda x: not isinstance(x, (int, float)) or x >= 5, another_list)

In the general case, you could have used a conditional expression; remember that an expression must produce a value. An if statement doesn't have to have an else branch, but that won't do in an expression. In your case your lambda must return a value to filter by, so you'd use

True if not isinstance(x, (float, int)) else False if x < 5 else True

but both isinstance() and the < comparison operator already produce a boolean result.

Instead of a lambda, it may be easier to use a separate function instead, to write out your condition in separate steps:

def everything_except_numbers_under_five(obj):
    # allow anything that is not a number
    if not isinstance(obj, (int, float)):
        return True
    # allow only numbers 5 or over
    return obj >= 5

final_list = filter(everything_except_numbers_under_five, another_list)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

Like Martijn said, in this specific example you could fit what you want into a lambda function. However, if in the future you want to make a filter with conditions that don't fit cleanly in a lambda function, you could make a separate function and pass it in to filter as an argument. Try something like this:

def filter_funct(x):
    if isinstance(x, (int, float)) and x < 5:
        return False
    # Plenty of space for more if or elif statements!
    return True

final_list = filter(filter_funct, another_list)
Simon
  • 142
  • 6