On Apr 10, 2020, at 13:29, Elliott Dehnbostel <[email protected]> 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.

_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/FMLLOLBDMV2YUT4EU3HTGSYOXUQCBA2Z/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to