attaching names to subexpressions
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Sat Oct 27 20:49:19 EDT 2012
On Sat, 27 Oct 2012 15:03:38 +0200, Gelonida N wrote:
> line = complex_exression
> while line:
> do something with(line)
> line = complex_expression
>
> violates clearly the DRY principle and the risk to change one line but
> not the other is high.
Only if the change is being made by an incompetent programmer.
DRY is not a law of physics. Not every expression needs to be written
once and exactly once or your code will become an unmaintainable mess. In
the example shown, the two instances of the expression are separated by
TWO lines -- if somebody edits one without editing the other, they are
probably not fit to be editing the code in the first place.
Repeated code is a code-smell, not a crime. You should always be on the
look-out for ways to minimise repeated code, but that doesn't mean every
time you see some trivial chunk of code you need to refactor it out.
If "complex_expression" actually is complex, then it should be factored
out into a function:
line = function(x, y, z)
while line:
do something with(line)
line = function(x, y, z)
so that the actual complexity is in one place, and any changes to it
needs to be made in one place. If it is not actually complex, then who
cares? So what if you have to write "line = next(iterator).strip()" twice?
If the body of the while loop is big enough that there genuinely is a
risk that you won't notice the two assignments to line, then you should
refactor the body of the while loop. Or change the loop:
while True:
line = function(x, y, z)
if not line:
break
big ugly block of code
There you go. DRY.
There is nothing in this proposal for while assignment that cannot be
*trivially* solved using existing syntax.
> The 'canonical way'
> while True:
> line = complex_expression
> if not line:
> break
> do_something_with(line)
>
> avoids this problem, but I was never really convinced about the beauty /
> readbility of this construct.
It's sure as hell more beautiful and readable than assignment as an
expression.
> One misses the exit condition of the while loop on the first glance.
If we are going to judge code on the ability of people to take a quick
glance and immediately understand it, then pretty much nothing but
trivial one-liners will pass. Certainly assignment as an expression won't:
while (look_ahead(it) and next(it) or default).strip().lower() as line:
spam(x)
is easy to miss that it is an assignment to x. *Much* easier than missing
an indented "break".
> In
> my opinion I shouldn't be obliged to read any of the indented lines of
> the while statement on a first 'visual' pass through somebody elses code
> and still be able to see what the loop iterates through.
Fine. Then write your code as:
line = function(x, y, z)
while line:
do something with(line)
line = function(x, y, z)
> Following looks in my opinion nicer.
>
> while complex_expression as line:
> do_something_with(line)
That's only because you don't actually have a complex expression. Just
writing "complex_expression" doesn't make it complex -- it's actually
trivially simple, a mere rebinding of a name to a name, no more "complex"
than
while x as y:
...
See how easy to read it is! Well duh. But write out an *actual* complex
expression, and the "as name" can easily disappear into the noise.
Complex expressions are a code smell at least as bad as violating DRY,
and frequently much worse. If the expression is too complex to write
twice, refactor it into a function, *and write tests for the function*.
Otherwise that complex expression is a bug waiting to bite, whether you
write it once or twice.
--
Steven
More information about the Python-list
mailing list