[Tutor] use of assignment expression

dn PyTutor at DancesWithMice.info
Mon Aug 17 01:03:41 EDT 2020


On 17/08/2020 01:55, Manprit Singh wrote:
> Dear sir ,
> i need  a comment on the use of assignment expression in the below given
> example :
> 
> # Program to find smallest n digit number divisible by a number x
> n = 5
> x = 83
> if (no := (10**(n-1) % x)) == 0:
>      print(10**(n-1))
> else:
>      print((mx := (10**(n-1) + x)) - (mx % x))
> 
> The answer is 10043, which is the right answer . Just need to know if the
> way of using the assignment expression (2 times in the above example is
> efficient or not.),
> 
> What i feel is the usage of assignment expressions is reducing the
> readability of the code written above  . Need your guidance.


Have you read the other responses? Here's an alternate point-of-view:-

Firstly, this is an arcane calculation. Whilst it may well reveal the 
meaning of the universe*, it seems (to me) that the people who talk like 
this are mathematicians, not (?mere) ordinary folk. Therefore, if the 
bunch of likely users (or more reasonably: the coders and users likely 
to ever read/check the code) are happily-capable of keeping stuff like 
this in their heads, "readability" is in the eye of the beholder! I 
don't like it. I'd break the formula into components - but I am not that 
sort of mathematician!

Secondly, "efficiency". If we execute this calculation 
once-in-a-blue-moon*, none of the re-statements proposed will make a 
noticeable difference. However, ... if this is to become a function 
which is executed hundreds of times within some larger calculation, eg 
what will the weather be like tomorrow? or, is there some new/uncharted 
star in this image of the galaxy? then the question of "efficiency" 
becomes (that many "hundreds of times") more important! Efficiency is 
not something to retro-fit. If the application requires efficiency, then 
it is 'in the spec' from-the-word-go!

That said, "readability" is often in a trade-off with efficiency!


We were introduced to the word "debuggability". Great choice: "able to 
be debugged". Let's try making our code 'testable', which puts it on the 
way to being "provable" (a term in math - just to show that I often talk 
with mathematicians - some of my best friends are ...). So, following 
the precepts of TDD (Test-Driven Development*) - which should be 
enormously popular in the world of mathematical computing (because of 
its similarity to developing a "proof"), let's start with:-

# test_divisor.py
import divisor  # you'll think of a much more meaningful name!
n = 5
x = 83
assert divisor.divisor( n, x ) == 10043
# later add a bunch more tests
# especially for the opposite calculation
# plus any/all the edge-cases/exceptions you can imagine
...

Having written a test, which will fail at the import, let's 'fix' that:


Funny ha-ha! Now execution/testing fails at the assert, because there is 
no such function:-

# divisor.py
# Program to find smallest n digit number divisible by a number x

def divisor( n, x ):
     if (no := (10**(n-1) % x)) == 0:
         print(10**(n-1))
     else:
         print((mx := (10**(n-1) + x)) - (mx % x))

The test fails because the assert 'complains' that returning None is 
incorrect.


Let's pause to discuss "testability". SRP (Single Responsibility 
Principle) applies - code each function/method to perform one task (and 
to perform it well). The function as it stands (as directly copied from 
the OP's code - with due apologies) performs TWO tasks: the calculation, 
and the printing of its result. Trivial? Agreed! However, principles are 
principles - and this is a toy-example. If we remove the second task, 
the function becomes thoroughly 'testable'. It has one task to complete, 
and we can easily check to see if the task has completed, and has 
delivered the correct response:-

def divisor( n, x ):
     if (no := (10**(n-1) % x)) == 0:
         return 10**(n-1)
     else:
         return (mx := (10**(n-1) + x)) - (mx % x)

Does that first test now work? Yes!
(only because our brilliance outshines the sun...)

BTW: add an if __main... and print( divisor(...) ) to divisor.py, and 
you'll be able to see where that second task (printing) 
currently-belongs - in case you were wondering.


One of the other components of TDD is to re-factor (both code-code and 
test-code). One component of re-factoring is - wait for it - 
"efficiency". Sure, most of us, happily deciding to use an 
interpreted-language, don't worry too much about this - but there will 
still be folks whose eyes start to bulge* at such a blasé assumption - 
and failure to accommodate (their) "diversity"!

Personally, I favor the virtue of putting the base-calculation before 
the if-statement. However, I'm a simple boy, and thus not a 
mathematician. If you are, and you are comfortable with reading the 
expression as a single thought (as are your colleagues) then your 
opinions of "readability" and "complexity" will be very different from 
mine! (psychology: "chunking")


Having stood-upon principle (SRP) earlier, let's pull out another. How 
about DRY (Do not Repeat Yourself)? Few coders (judging from the worst 
of the YouTube demos) are typists - and can I/dare I say, even fewer 
mathematicians. So, if the expression "10**(n-1)" has to be entered into 
the code-base three (or more) times, what are the chances of a typo 
creeping-in somewhere?

Talking about "debuggability"? The best debugging is to obviate its 
need! Quoting some commentary from my over-the-fence conversation with a 
neighbor this morning: "best to keep your mouth closed and be thought a 
fool, than to open it and remove all doubt!"* (no he didn't have you (or 
I) in-mind) Re-stated for our use: 'best not to type, if you don't have 
to'. Thus: type it once, make it test-able, test it, and then re-use 
with confidence!


The time has come, to talk about the walrus (with apologies to Lewis 
Carroll*). An <<<assignment expression (sometimes also called a “named 
expression” or “walrus”) assigns an expression to an identifier, while 
also returning the value of the expression.>>>* The PEP's rationale, 
better-explains its advantages/purposes as <<<Naming the result of an 
expression is an important part of programming, allowing a descriptive 
name to be used in place of a longer expression, and permitting reuse.>>>*

There's that "re-use" again - and my DRY sense of humor. Hah!

The OP twice demonstrates the named-expression's syntax but in the first 
case, sadly, fails to benefit from its purpose (as observed in another 
response). The "reuse" advantage/rationale was lost.

Which part(s) of the calculations are candidates for reuse? Well, 
"10**(n-1)" appears three times. So let's start there. I would go for:

     n_digit_number = 10**(n-1)
     if...

but, in-keeping with the OP's (apparent) preferences, the 
all-in-one/walrus approach is:

     if ( (no := 10**(n-1) ) % x) == 0:

NB only a mathematician could love a variable called "no". Do I like it? 
No! Do I (primarily) speak a language where the word "number" includes 
the letters "N" and "o". No! 98% of Norwegians likely also agree.


TDD-ers: run a test to assure the refactoring work! Then we can move-on to:-

     if...
         return no  # saving one re-computation
     else:
         return (mx := no + x) - (mx % x))
         # the use of no saving another re-computation, plus
         # using mx already demonstrates how it should be done!

Get your TDD-on, and ensure that this is correct...


There is no point in using a named-assignment if it is not subsequently 
re-used - any more than would be the case for other types of assignment. 
Conversely, if the same computation is required at multiple points in 
the code, then the 'walrus operator' is another arrow in your quiver*.


NB I'm not running Python v3.8. Accordingly have yet to build-up an 
opinion based upon experience!


Web.Refs
apologies, not Monty Python: https://en.wikipedia.org/wiki/42_(number)

https://www.dictionary.com/browse/once-in-a-blue-moon

https://www.agilealliance.org/glossary/tdd/#q=~(infinite~false~filters~(postType~(~'page~'post~'aa_book~'aa_event_session~'aa_experience_report~'aa_glossary~'aa_research_paper~'aa_video)~tags~(~'tdd))~searchTerm~'~sort~false~sortDirection~'asc~page~1)

Surprise! 
https://image.shutterstock.com/image-photo/image-450w-228517990.jpg

https://www.quora.com/What-does-the-phrase-open-your-mouth-and-remove-all-doubt-mean?share=1

https://forum.wordreference.com/threads/stand-on-principle.2206751/

https://www.goodreads.com/quotes/6028-the-time-has-come-the-walrus-said-to-talk-of

https://docs.python.org/3/whatsnew/3.8.html#assignment-expressions
https://docs.python.org/3/reference/expressions.html?highlight=named%20expressions#assignment-expressions
https://www.python.org/dev/peps/pep-0572/

https://idioms.thefreedictionary.com/an+arrow+in+the+quiver
-- 
Regards =dn


More information about the Tutor mailing list