[Tutor] Valid Username Program

dn PyTutor at DancesWithMice.info
Thu Oct 8 19:17:38 EDT 2020


On 09/10/2020 03:44, Aliyan Navaid wrote:
> Since i'm a beginner could you please give examples for the last part ?
> where you list all the possible scenarios.
> 
> On Thu, Oct 8, 2020, 6:17 PM Alan Gauld via Tutor <tutor at python.org> wrote:
> 
>> On 08/10/2020 12:29, Aliyan Navaid wrote:
>>>     def valid_username(username):
>>>         if len(username) < 3:
>>>             print("Username must be atleast 3 characters long")
>>>         elif len(username) > 15:
>>>             print("Username is too long")
>>>         else:
>>>             print("Valid Username")
>>
>>>     I wanted to ask that instead of elif, I could’ve written another If
>>>     statement in this program right ?
...

>> But those are the exception rather than the rule, in most scenarios
>> elif is the safer option.

+1


In 'the ?good, old, days' you would have been asked to draw a flowchart 
of the decision(s). Such a 'picture' quickly illustrates @Richard's 
point - neatly covered by 
http://web.engr.oregonstate.edu/~rookert/cs162/ecampus-video/CS161/template/chapter_4/ifelse.html


In the case of a single "independent-variable" (len( username ), above), 
we can code a "ladder" of if and elif clauses (probably terminating with 
an else). Here we have such a short "ladder" it is barely recognisable 
as such - we'll come back to that later.

Let's split the problem into its components (as per code, above):

1 the "independent variable": username_length = len( username )

2 what should happen if the username_length is less than three-characters.

3 what should happen if the username_length is more than 15-characters.

4 what should happen if the username_length is not either of the "3" or 
"15" considerations.


The first suggestion is to use a value: username_length; in that it is 
better to compute something once, rather than hundreds of times 
(appreciating that in this 'toy example' the difference is 
inconsequential, but let's call it "good practice" or a habit worth 
cultivating). Also, it simplifies explanations to have a single-word 
description.

The second suggestion is to pick-up the hint in using the term 
"independent variable". We compare it to three values: 3, 15, and 
'between'. Let's express them in a more logical sequence: "3, between, 15"

This leads us neatly into @Alan's suggestion of a function which returns 
three different results, as appropriate:

def username_length_decision( username_length ):
     if username_length < 3: return "short"
     if username_length < 15: return "valid"
     return "long"


Reverting to the flowchart, and comparing that to this re-statement of 
the code, can you start to see the "ladder"? If not, code imaginary 
alternative actions to take place at 20 and 25. The study-example which 
is popularly used is to look at converting percentage grades for 
student-assignments into a letter ("A" (very good) through "F" (failure) 
) wherein there are five decisions to be made (if or elif) and six 
alternate results. Try flowcharting and/or coding that one...


To finish, let's look at the more advanced issue: the more general 
solution, and the more numerous case. How would you like to code a 
procedure which requires a choice to be made amongst 100 different 
levels? Indeed, even the student-grades application mentioned, starts to 
make my typing-fingers weary! Is there another (pythonic) solution? (of 
course there is!)

- starting from the specification outlined above, def[ine] a function 
for each (distinct) action that could be carried-out under the control 
of this decision, eg

def short()...
def valid()...
def long()...
(etc)

NB this approach is a good coding habit, called "modular programming".


...and here's the 'trick': in Python a function is a "first class 
object". We can quietly ignore that ComSc definition by understanding 
that a function can be treated as if it was a piece of data. This is an 
informal or "rule of thumb", but it will do for today.

This enables us to put these three, six (or hundreds) of function names 
together with the username_length "independent variable". Create a 
dictionary and populate it with "pairs": the "independent variable" and 
its ("dependent") action-function:

decision_dict = {
     3:short,
     15:valid,
     }

Now, faced with a decision to be made, we can loop through the 
dictionary, and apply the "independent variable" to each key, and once 
the key is chosen, execute the appropriate function:

for key_username_length, action_function in decision_dict.items():
     if username_length < key_username_length:
         action_function()
         break

The first time through the loop is exactly:

     if username_length < 3:
         short()

and if it that condition is true, the break saves us from looping any 
further.


It's as easy as that, because
- there's only one "independent variable" driving the choice,
- the choices all use the same boolean comparison, and
- the choices can be logically-sequenced.


Oh but wait, is something missing?

Yes! We've covered everything that used to be in an if or elif clause, 
but what about the else-choice, ie >15 characters?

Here we apply another advanced and pythonic solution: Python's for-loops 
include an optional else-clause too. In this case, (only) if the loop 
terminates without break-ing, will control pass to the else-clause.

Thus:

for key_username_length...
     if ...
else:
     long()


WebRefs:
https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
https://book.pythontips.com/en/latest/for_-_else.html

Warning: the use of else clauses after for- (or while-) loops is widely 
misunderstood (pronounce the "else" as: 'else if no break'). For this 
reason, most try not to use them. However, if more of us learned their 
proper use, that would cease to be a concern. That "which came first, 
the chicken or the egg" problem is something for another day...


PS If the solution depends upon multiple dependent-variables then please 
review https://en.wikipedia.org/wiki/Decision_table
-- 
Regards =dn


More information about the Tutor mailing list