On Apr 10, 2020, at 13:29, Elliott Dehnbostel <pydehnbostel@gmail.com> wrote:

We could do this:
chars = "abcaaabkjzhbjacvb"
seek = {'a','b','c'}
count = sum([1 for a in chars if a in seek])
However, this changes important semantics by creating an entire new list before summing.
It sounds like you really are just looking for generator expressions, which were added in Python 2.4 a couple decades ago. They have the same syntax as list comprehensions without the square brackets, but they generate one element at a time instead of generating a new list. If that’s your only problem here, just remove the square brackets and you’re done.
Also, adding just one more expression to the most nested block thwarts that refactor.
No it doesn’t:

    count = sum(1 for a in chars if a.isalpha() and a in seek)

And you don’t have to stop with adding another expression; you can add a whole new if clause, or even a nested for clause:

    count = sum(1 for s in strings if s.isalpha() for a in s if a in seek)

Of course if you add too much, it becomes a lot harder to read—we’re already pushing it here. But in that case, you can always pull out any subexpression into a function:

    def matches(s):
        return (a for a in s if a in seek)
    count = sum(1 for s in strings for a in matches(s))

… which can often then be simplified further (it should be obvious how here).

Or, even better, you can usually create a pipeline of two (or more) generator expressions:

    chars = (a for s in strings for a in s)
    count = sum(1 for a in chars if a in seek)

(And sometimes it’s natural to replace some of these with map, itertools functions, two-arg iter, etc.; sometimes it’s not. Do whichever one is more natural in each case, of course.)

The great thing about this style is that you can pipeline a dozen transformations without any nesting, and without adding any confusion. They just pile up linearly and you can read each one on its own. David Beazley’s “Generator Tricks for Systems Programmers” presentation has some great examples, which demonstrate it a lot better than I ever could.

And of course you can always fall back to writing nested block statements. The nice declarative syntax of comprehensions is great when it’s simple enough to understand the flow at a glance—but when it isn’t, that usually means you need a visual guide to understand the flow, and that’s exactly what indentation is for. And if there are too many indents, then that usually means you should be refactoring the inner part into a function.

Sure, “usually” isn’t “always”, both times. There are some cases that fall maddeningly in between, where a sort of hybrid where you had nested blocks but could collapse a couple together here and there would be the most readable possibility. I know I run into one a couple times/year. But I don’t think those cases are really much more common than that. Any new feature complicates Python—and this one would potentially encourage people to abuse the feature even when it’s really making things less readable rather than more.

Comprehension syntax is great _because_ comprehensions are limited. You know you can read things declaratively, because there can’t be any statements in there or any flow control besides the linear-nested clauses. That doesn’t apply to nested blocks.