Advanced Python series (part 2)

Michael Ngecha
4 min readFeb 12, 2023

--

This is the second part of the Advanced python series where we understand some advanced python concepts. You can find the first one here. In this article, we will look at:

  1. Type Hinting
  2. Encapsulation
  3. Argument parsing
eat_sleep_code_repeat
Photo by Roman Synkevych 🇺🇦 on Unsplash

Lets get into it, shall we?

Type Hinting

Python is a dynamically typed language. That means we don’t have to declare a variable or function with its type. The type of a variable is assigned by the interpreter at runtime depending on the value of the variable at the time. There is, However, a way of(sort of) statically typing python code by adding types to variables, parameters, function arguments, return values and even class attributes. Here’s an example;

name: str = "Mike

In a function, it would look more like:

def myfunction(param: list[int]) -> str:  #Here, type of parameter and return value is specified
for i in param:
print(f'{i} is in {param}')

Type annotations of variables and function arguments are written after a colon symbol, while return type follows( -> ).
Cool right? Sadly, type hints are not enforceable :( so your python code will still run even when the wrong type is provided (at the time of writing this). Type hinting does not have any effect on runtime, It just makes you look more nerdy writing it.

Encapsulation

encap
Photo by danilo.alvesd on Unsplash

This is one of the concepts of Object Oriented Programming. It involves keeping methods and attributes of a class private to that class, then controlling access by any external objects or methods. You don’t get it?, That’s fine. Let’s start by defining a class.

class Person:
def __init__(self, name, age):
self.__name = name # double underscore prefix makes
self.__age = age # these properties private.

The class has attributes name and age, which are set to private by adding a double underscore prefix to the attribute.

To access private properties of a class, special functions called getter functions are used. Changing values of these properties also require special functions called setters. Here is a getter for the class above.

class Person:
def __init__(self, name, age):
self.__name = name # double underscore prefix makes
self.__age = age # these properties private.

@property
def Name(self):
return self.__name

Attributes of this class can only be accessed through the Name() function. You know it is a getter function because it has the ‘@property’ decorator directly above it. Since the getter function returns only the ‘name’ attribute, that’s the only information about the class that can be accessed.

To assign values to attributes, a setter function is used as shown here:

 class Person:
def __init__(self, name, age):
self.__name = name # double underscore prefix makes
self.__age = age # these properties private.

@property
def Name(self):
return f'Name: {self.__name}'

#setter function allows us to give conditions for changing values
@Name.setter
def Name(self, value):
if value == "Bob":
self.__name = "Cannot be Bob"
else:
self.__name = value

Notice how both getter and setter function have the same identifier(function name). The difference is that the setter will takes an argument.

Using a setter function, we can control who alters values, what values are acceptable or any other strange conditions you might think of. Lets set and get a name to the class

p1.Name = "Joan"  # calls setter to change name property to Joan
print(p1.Name) # calls getter to return name propertyp
Name: Joan.

Now lets try to set the name value to Bob even after we have explicitly declared that name cannot be Bob.

p1.Name = "Bob" # calls setter to set name to Bob
print(p1.Name)
Name: Cannot be Bob

Getters and setter functions give control over access of private attributes of a class. This might be data that needs to be secure like addresses that you wouldn’t want in the wrong hands.

Argument Parsing

I’m sure you have seen bash command that looked like:

python3 args.py -f 'newtxt.py' -m 'print("hello world")'

You really wondered how they make it do that. well, we will see.

The above line runs a python3 file called ‘args.py’ and takes arguments -f and -m. Lets see how its done.

It’s always geeky to have some imports.

import sys        
import getopt

Lets set some default values, useful in the off-chance that no parameters are passed.

filename = 'text.txt'
text = "Hello"

Nerdy python code coming up:

opts, args = getopt.getopt(sys.argv[1:], "f:m:", ["filename", "message"])

getopt.getopt() returns a tuple with options and arguments for a command line command. sys.argv[] is an array method for command line. Further reading on these functions is highly recommended for better understanding.

The following code loops through key value pairs in opts, setting what the value of each argument should be.

for opt, arg in opts:
if opt == "-f":
filename = arg
if opt == "-m":
text = arg

with open(filename, 'w+') as f:
f.write(text)

arg represents the value passed in the command.

Looking at the command we started with:

python3 args.py -f 'newtxt.py' -m 'print("hello world")'

python 3 file named args.py is called with argument -f (filename) given as ‘newtxt.py’ and -m (message) given as ‘print(“Hello World)’. The command opens newtxt.py file. If it doesn’t exist, a new one is created. The message is then written in the opened file, and now you don’t have to right click to create a new file like the average human person.

This is all cool stuff to be doing as a python programmer. Whether its to optimize you workflow or just to make you look cooler than others, i would definitely advice you to use these techniques. Be careful not to get negative reviews on your code though. In the next part of the series, we take on design patterns.

Happy Coding! :)

--

--

Michael Ngecha

Michael Ngecha is an aspiring machine learning engineer and skilled python developer who has a background in web development. Contact: ngechamike26@gmail.com