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

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 = dispatchtable.get(cls) if reductor: rv = reductor(x) else: reductor = getattr(x, "reduceex", 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 := dispatchtable.get(cls): rv = reductor(x) elif reductor := getattr(x, "reduceex", 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