In Python, I often find myself implementing the same pattern: count the number of "valid" iterations while processing within a loop, where an "invalid" iteration is skipped over with a continue statement. I use the continue statement instead of if-else
blocks to improve readability. Essentially, I do the following:
count = 0
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
count = 1
do_something_2(item)
return count
There are several nifty tricks one can use to implement similar patterns in a Pythonic manner. For example, enumerate
, continue
, break
, for-else
, and while-else
come to mind. I am looking for a Pythonic construct to implement the scenario described above.
This works (below) but would require the evaluate_some_condition
function be executed twice for every element, which can sometimes be unacceptable (it is also less readable in my opinion):
count = sum(1 for item in collection if not evaluate_some_condition(item))
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
do_something_2(item)
return count
Some construct like the below would be ideal:
for count, item in uninterrupted_enumerate(collection):
do_something_1(item)
if not evaluate_some_condition(item):
continue
do_something_2(item)
return count
Any ideas of a built-in Python feature, third-party feature, or future plans to include such a feature?
CodePudding user response:
No, I think the very first version:
count = 0
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
count = 1
do_something_2(item)
return count
is pretty much how it is done and it very obvious (i.e. readable) what is happening. If you compare it with your last version, where it would be very unclear that any counting is happening. If you really want to solve it that way, you could always increase some global
variable within the evaluate_some_condition
function, depending whether the internal check was successful or not. But again, this is probably less readable than your first version.
CodePudding user response:
I think the most interesting alternative is to use decorators. Because this was described in a different post, I will simply provide the link: https://stackoverflow.com/a/44969343/8033585
Another alternative is to use classes. For example:
class Doer2:
def __init__(self):
self.count = 0
def __call__(self, item):
self.count = 1
# put here the code from 'do_something_2()'
....
Then:
doer2 = Doer2()
for item in collection:
do_something_1(item)
if not evaluate_some_condition(item):
continue
doer2(item)
return doer2.count