So The Newbie Created a Utility File? (Who wrote that code?)

Michael Otu - Jan 29 '22 - - Dev Community

We discussed the generate_token function and how I'd write it today. In this part, we will look at a snippet that was a mistake or better, a deviation. The function implementation is kind of different from the doc string, below:

"""
Returns the number of specific characters in data that is 
in the sack
By default, data is an empty string and sack is an empty list
Both data and list, are iterable
If neither data nor sack is set, zero in returned
"""
Enter fullscreen mode Exit fullscreen mode

get_schar_count

Can you implement a function for the doc string above? Give it a try without looking at mine.

We are all learning from the success and mistakes of others so do not worry or feel down if you couldn't.

Ok, my snippet at that time looks like this:


def get_schar_count(data='', sack=[]):
    """
    Returns the number of specific characters in data 
    that is in the sack
    By default, data is an empty string and sack is an 
    empty list
    Both data and list, are iterable
    If neither data nor sack is set, zero in returned
    """
    if not data or not sack:
        return 0

    counter = 0

    for char in data:
        if char in sack:
            counter += 1

    return counter

Enter fullscreen mode Exit fullscreen mode

I am surprised. This function completely deviates from its functionality (Comparing the doc string to the actual code).

So what does this snippet do?

To make no assumptions, let's ignore the content of the doc string.

  • If either data or sack is not given return 0
  • Initialize counter variable with a value of 0
  • Looping through data with (or using) char as a loop pointer, check if char is in sack then increment counter variable by 1 else do nothing (move to the next iteration)
  • Return counter at the end of the loop

From there, it is obvious what get_schar_count is meant to do or does.

Let me make it simple (not easier). Assume that we want the user password to contain certain characters. The password must be just lowercase alphabets and maybe a single number say '1' (This 1 is a string since it is part of a string - the password). Then sack will be, ['a', 'b', 'c', ..., 'z', 1]. At the end of the loop, if counter is less than len(data) then the password (in this case, the data) provided is invalid. So the counter that is returned from the function is the number of characters in data that are in sack.

Have you noticed the conflict or the deviation yet?

The Deviation

What do you call a function that works but does not do what it is supposed to do?

Let's have a look at the doc string again.

"""
Returns the number of specific characters in 
data that is in the sack
By default, data is an empty string and sack 
is an empty list
Both data and list, are iterable
If neither data nor sack is set, zero in returned
"""
Enter fullscreen mode Exit fullscreen mode

This is like a frequency counter. A frequency counter - counts the number of unique individual elements in a list, string, etc. I think I wanted it to return a dictionary (an object) or something like that. I didn't want the user's password to have repeated characters. get_schar_count rather checks (counts) if the data supplied is valid (in the sack).

Some Implementations

I came up with some implementations.

Snippet 1

def get_schar_count(data):
    schar_count = {}

    for char in data:
        if char in schar_count:
            schar_count[char] += 1
        else:
            schar_count[char] = 1

    return schar_count
Enter fullscreen mode Exit fullscreen mode

Snippet 2

def get_schar_count_dict(data):
    schar_count = {}

    for char in data:
        char_count = schar_count.get(char, 0)
        schar_count[char] = char_count + 1

    return schar_count
Enter fullscreen mode Exit fullscreen mode

Snippet 3

def get_schar_count(data):
    schar_count = {}

    for char in data:
        if char not in schar_count:
            schar_count[char] = 0

        schar_count[char] += 1

    return schar_count
Enter fullscreen mode Exit fullscreen mode

So What Is Different?

These snippets do the same thing. They are the same thing. The first could look much like the second with a little tweak.

Assuming we passed 'hello' as data an output similar to that below will be generated.

{'h': 1, 'e': 1, 'l': 2, 'o': 1}
Enter fullscreen mode Exit fullscreen mode

If The Original Works, Could It Be Used?

The original snippet can be used even though it deviates from its purpose. It serves another valid purpose as mentioned above, it can be used to validate a password, in our case. That is if the function head was, def get_schar_count(data='', sack=[]):...

Let me make it simple (not easier). Assume that we want the user password to contain certain characters. The password must be just lowercase alphabets and maybe a single number say '1' (This 1 is a string since it is part of a string - the password). Then sack will be, ['a', 'b', 'c', ..., 'z', 1]. At the end of the loop, if counter is less than len(data) then the password (in this case, data) provided is invalid. So the counter that is returned from the function is the number of characters in data that are in sack.

From the above, we can say that the original snippet can be used to validate the user password. As far as the value returned, counter, is of the same size as data, then all the characters in data is in sack, hence, data is valid, else invalid.

Re-Implementation of the Original Snippet

I would have to rewrite the original with a few changes.

def is_password_valid(data='', sack=[]):
    """
    Returns True when all the characters in data are 
    in sack else False
    By default, data is an empty string and sack is 
    an empty list
    Both data and list, are iterable
    If neither data nor sack is set, False in returned
    """
    if not data or not sack:
        return False

    for char in data:
        if char not in sack:
            return False

    return True
Enter fullscreen mode Exit fullscreen mode

Lesson

I wasted my resources on this function, thinking and writing the code. If I had thought well of a use case, then the function, get_schar_count, would make sense to me now and ever. To avoid such headaches:

  • Write a unit test at least for your functions. This way if the function is not useful, you'd realize it and remove it from the codebase entirely.
  • Before you create a function, make sure that that functionality is needed. Now use the function in your code before implementing it. This is just like writing tests before implementation.
  • "get_schar_count"? What? Find a better descriptive name (I maintained it because it did the work).
  • There is a one-liner approach that to me it is good but for the sake of readability, I'd choose another approach.

    def get_schar_count(data):
      return { char: data.count(char) for char in set(data) }
    
  • If you noticed, none of the snippets above modified the data passed to it. The data argument/variable was not modified. At least, in your dysfunctional code, don't modify some data.

  • As a programmer, you have the privilege to be forgetful. It's okay, make use of comments, in fact, a .md file for comment just for some function or file, you don't know when you will come back to the code again. Again, this is just an exaggeration.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .