Why does "return s and s.strip()" work when using filter?

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# result: ['A', 'B', 'C']

I don't know why this worked. I know that : x and y if x is false, then x, else y. So first is return 'A' and 'A'.strip(). But this showed in python IDLE

>>>'A' is True
False
>>>'A' is False
False

So. not_empty('A') returned 'A', right? 'A'is not True, why is it in the result?

And why

not_empty(' ')
''

' ' is also False.

>>>' ' is True
False
>>>' ' is False
False

2 answers

  • answered 2017-08-21 14:38 cᴏʟᴅsᴘᴇᴇᴅ

    Translate your filter function into a good ol' for loop:

    old = ['A', '', 'B', None, 'C', '  ']
    new = []
    
    for i in old:
        if i and i.strip():
            new.append(i)
    
    print(new)
    

    Outputs:

    ['A', 'B', 'C']
    

    Why this happens is because of the if which evaluates the truthiness of expressions.

    You should know that all empty iterables (including the empty string ''), 0, False, and 0.0 are all evaluated to False in an expression. There are some space strings, which when stripped, are reduced to the empty string which are also evaluated to False.

    Going over each element, this is how your program runs:

    Initial
    old = ['A', '', 'B', None, 'C', '  ']
    new = []
    
    Iteration 1
    i = 'A'
    'A' and 'A'.strip() is evaluated to True
    new = ['A']
    
    Iteration 2
    i = ''
    '' is evaluated to False (short circuit)
    new = ['A']
    
    Iteration 3 (same as 1)
    i = 'B'
    ...
    new = ['A', 'B']
    
    Iteration 4
    i = None
    None is evaluated to False
    new = ['A', 'B']
    
    Iteration 5 (same as 1, 3)
    i = 'C'
    ...
    new = ['A', 'B', 'C']
    
    Iteration 6
    i = '  '
    ' ' is True but ' '.strip() is False
    new = ['A', 'B', 'C']
    
    Final
    new = ['A', 'B', 'C']
    

  • answered 2017-08-21 14:44 Matthew Ciaramitaro

    First let's do some basic debugging.

    "A" and "A".strip() prints "A" to the shell

    Now why does it do that. Well let's look up how and works

    see here

    "A and B returns A if A is False, and B otherwise"

    So since "A" is not 0 or False, not_empty will return "A".strip(). now "A".strip()=="A", So the not_empty returns "A"

    Similarly, when filter is called, it will apply the function and check the result, and each time it will not get False. because of this, it will accept the value into the new list.