Reputation: 4507
I'm running Python 3.
Is it possible to put a loop in the condition for elif
? Here's the basic idea I'm trying to achieve. The number of items in the list is not constant.
if some_condition:
do this
elif [ x.method() for x in list ]:
do this to x
else:
do something else
Now, this comes to my mind:
if some_condition:
do this
for x in list:
if x.method():
do this to x
break
But I'm trying to avoid running all the if statements, there's a lot of stuff going on in them. And I would like to get it in the elif
part specifically and not in else
.
It seems what I would need is any( x.method() for x in list )
but also with a reference to x
so that I can then use x
if the condition was true.
Here's the whole concept I'm trying to get again:
if condition:
do this
elif list[0].method():
do this to list[0]
elif list[1].method():
do this to list[1]
...
elif list[n].method():
do this to list[n]
else:
do this
where method()
is some method that returns True
or False
, and n
is the size of the list and not a constant.
Upvotes: 1
Views: 4445
Reputation: 1475
I see now. The Pythonic way of doing what your updated edit says is to make a for loop and break out after you find your first item and act on it, and use the almost unknown and unused "else" of a loop.
Since the "for / else" idiom is so rarely used, you will need to add a comment about it, for future readers.
if some_test:
first_action()
else:
for item in list:
if item.method():
do_this(item)
break # process no more of list
else: # for-else only entered if no break from the list!
final_action()
Read up on loop elses in the Python Tutorial.
Upvotes: 3
Reputation: 744
This could work as long as you don't need a reference to x outside of the elif block. This feels closest to the syntax you are asking for, but I would still probably go with either Kevin or Tobias's answers as they are more explicit and readable.
def check_and_do(x):
b = x.method()
if b:
do this to x
return b
if condition:
do this
elif any(check_and_do(x) for x in list):
pass # Already did this to x in check_and_do
else:
do something else
Upvotes: 0
Reputation: 82899
I don't think what you want -- to have it entirely in the elif
-- is possible. You'd have to evaluate whether there is any
such value in the list, and then bind it to x
in the condition. As far as I know, this is not possible in Python's syntax. You can not do an assignment in the condition, and while the loop variable in a list comprehension can "leak" to the outside scope, the same is not true for a generator.
>>> if any(x for x in range(10) if x >= 5):
... print x
NameError: name 'x' is not defined
>>> if any([x for x in range(10) if x >= 5]):
... print x
9
In the second case (list), we have a reference to x
, but it is the last value from the entire list, and in the first case (generator), x
can not be resolved at all.
Instead, here's another variant, using a generator expression to combine the for
with the if
, and adding an else
to the for
to enumate your final else
clause.
if some_condition:
print "do this"
else:
for x in (x for x in lst if foo(x)):
print "do this to", x
break
else:
print "do something else"
Upvotes: 2
Reputation: 10298
It is not exactly possible to do what you want, but you can do something similar with list comprehensions:
if some_condition:
do this
else:
res = [x for x in list if x.method()]
if res:
do something to res[0]
else:
do something else
To avoid do x.method()
for every value, you could also use itertools.dropwhile
. The following approach will keep checking x.method()
until it finds one that is true, then stop and return just that value of x
. If it doesn't find any that are true, it will do something else.
from itertools import dropwhile
if some_condition:
do this
else:
try:
res = next(dropwhile(lambda x: not x.method(), x))
do something to res
except StopIteration:
do something else
Upvotes: 0
Reputation: 1475
if [ x.method() for x in list ]:
You ask, is it possible? Yes. What does it do, though? Probably not what you intend.
For the number of items in list, it will fabricate one value in the resulting list. For list values, only a list of zero length will be False. [False, False, False] is True, so the body of the condition will be executed.
You may be looking for all() or any() if x.method() is supposed to influence the following condition decision.
if all(x.method() for x in list): # will require that every *x.method* call return truth.
Likewise, you may want
if any(x.method() for x in list): # only one required. May not do every .method if it can stop early. Beware side-effects.
Upvotes: 1