I took the liberty of rewriting most of this from scratch for a few reasons.
First, this very difficult to actually test. All your embedded if statements... oh my! Second it's also difficult to add new checks into if you want other filter conditions.
Code like your original code starts out well intentioned but becomes a giant spaghetti mess fairly quickly - you only have three conditions and it's already a complex and hard to read and quickly understand function.
I moved each conditional check into a separate, small function:
# Convention: returns true if should be removed, based on criteria
return any(char.isdigit() for char in s)
Any is a great use for things like this. It will return
True if any condition in the list matches.
if len(s.split(' ')) == 1:
return s.upper() != s
words = s.split(' ')
if len(words) > 1:
return words.upper() != words
Fairly straightforward here. Both these test specific conditions that can be optimized a bit better in single functions.
This can streamline your main logic significantly (using
filters = [contains_numbers,
filtered = 
for s in strs:
if not any(foo(s) for foo in filters):
Notice how easy it is now to add new check conditions, you just need to make/test a small function and add it to your filter list. Then iterate over that list, checking if any of them return true for the current string.
You could make this a list comprehension and even simpler if you want:
return [s for s in strs if not any(f(s) for f in filters)]
Adding new logical checks is suddenly trivial. Changing existing ones, similarly. With one main "if tree to end all if trees" you will over time end up with a complete mess.
This approach also has the added feature/benefit of making testing much more trivial (I used basic assert statements here):
assert(True == contains_multiple_words_not_uppercase('foo bar'))
assert(False == contains_multiple_words_not_uppercase('Foo bar'))
assert(False == contains_multiple_words_not_uppercase('Foo'))
assert(True == contains_numbers('123'))
assert(True == contains_numbers('1asdf23'))
assert(False == contains_numbers('Foo bar'))
assert(False == contains_numbers(''))
assert(False == single_word_is_not_all_caps('FOO'))
assert(False == single_word_is_not_all_caps('foo bar'))
assert(False == single_word_is_not_all_caps('Foo bar'))
assert(True == single_word_is_not_all_caps('Foo'))
assert(['FOO'] == filter_strings(['Foo', 'FOO']))
assert(['BAR', 'Foo bar'] == filter_strings(['Foo', 'foo', 'BAR', 'Foo bar', '123']))
assert(['FOO'] == filter_strings_list_comp(['Foo', 'FOO']))
assert(['BAR', 'Foo bar'] == filter_strings_list_comp(['Foo', 'foo', 'BAR', 'Foo bar', '123']))
This is a lot easier to isolate problems, particularly if this ends up with many filters.
I would recommend adding more tests here, there are conditions I'm not sure how you want to test or what they should return.