[Python-Dev] The new and improved PEP 572, same great taste with 75% less complexity!

Tim Peters tim.peters at gmail.com
Thu Apr 26 15:12:21 EDT 2018


[Kirill Balunov]
> Not sure, but if additional motivating examples are required, there is a
> common pattern for dynamic attribute lookup (snippet from `copy.py`):
>
>     reductor = dispatch_table.get(cls)
>     if reductor:
>         rv = reductor(x)
>     else:
>         reductor = getattr(x, "__reduce_ex__", None)
>         if reductor:
>             rv = reductor(4)
>         else:
>             reductor = getattr(x, "__reduce__", None)
>             if reductor:
>                 rv = reductor()
>             else:
>                 raise Error("un(shallow)copyable object of type %s" % cls)
>
> which can with the current `binding expression` syntax simplified to:
>
>     if reductor := dispatch_table.get(cls):
>         rv = reductor(x)
>     elif reductor := getattr(x, "__reduce_ex__", None):
>         rv = reductor(4)
>     elif reductor := getattr(x, "__reduce__", None):
>         rv = reductor()
>     else:
>         raise Error("un(shallow)copyable object of type %s" % cls)
>
> which becomes much clearer, at least in my opinion.

[Larry Hastings <larry at hastings.org>]
> I hate to be pedantic--there's enough of that going on in this thread--but I
> can't agree with the word "simplifed" above.  I agree that the code using
> binding expressions is shorter.  But considering that emit the two code
> examples implement the exact same algorithm, to the point where their
> bytecode would look nearly* identical, ISTM that the two code examples are
> of identical complexity.

In the absence of defining an objectively computable complexity
measure,  I expect you're doomed to arguing taste.  For example, argue
that both spellings have the same formal "cyclomatic complexity"
measure (which they do).  By other formal measures (e.g., total number
of identifier instances), the latter spelling is "objectively
simpler".  By yet others (e.g., total number of non-whitespace
characters divided by total number of lines), the former spelling is
"objectively simpler".

But that all kinda misses the point to me:  the latter spelling is
"obviously simpler" in a way that _actually matters_, for the same
reason, e.g., a case statement with N cases is "obviously simpler"
than the semantically equivalent spelling using N nested if/else
if/else if/else if/else ... blocks.  The latter spelling above is
indeed visually very much like a case statement:  all the tests are at
the same indentation level, and all the conditional actions are too.
It's obvious _at a glance_ in the latter that exactly one of the
action blocks will be performed.  That's how if/elif/elif/else always
works.  It's not at all obvious at a glance (certainly not to me) in
the original spelling.

> Comparing the two, the code using the binding expressions obviates four
> newlines, three uses of the identifier "reductor", and allows folding two
> "else / if"s into "elif"s.  In exchange, it adds three extra colons,

If it weren't for that you hate being pedantic, I'd add that you're
overlooking the piles of leading whitespace characters also saved in
the latter ;-)  The number of those saved grows quadratically in the
number of uselessly indented blocks shifted left.

> and the density of complexity per line has shot up.

Average non-whitespace character count per line has certainly shot up,
but I don't actually know what you mean by "density of complexity"
there.

Just FYI, when I write long if/elif/elif/... chains, I typically put a
blank line before each elif, to give better visual separation of the
peer (both semantically and visually) test-action blocks.  Which has
nothing to do with any formal notion of complexity, because I don't
much care about that - readability is what I value, and that's not the
same as any formal notion of complexity I've ever seen.

>  ...


More information about the Python-Dev mailing list