Python Primer — Functions and Objects

Marius Safta
Cluj School of AI
Published in
9 min readMay 7, 2019

Hello World,

After an overview of Python data types and then if and loop statements, we are ready to go over two very important concepts of any programming language, functions and objects.

Functions

Functions are named after their mathematical counterpart, because the principle is the same: take some input, perform operations and output a new value.

“Programming” functions take a set of parameters (can also be with no parameters), execute the statements inside and may return something (a data type, an object or even another function).

More importantly, functions are like building blocks of programming, because they allow the reuse of their code. So let’s see first how to define a function in Python and then how it applies the principle of code reuse.

Definition

A Python function is defined with the def keyword. We also need to specify a function name and optionally some parameters:

def function_name(parameter1, parameter2):    # some code here    pass

Parameters go inside the brackets after the name. As with if, for or while statements, the code block inside the function must be appropriately indented.

To run a function, we call its name and provide the necessary parameters (if there are any).

def hello_world():    print("Hello World")
hello_world()
hello_world()>> Hello WorldHello World

In the above example we:

  • Defined the hello_world function with no parameters
  • In the function code block we used print to write “Hello World”
  • Called the hello_world function twice
  • Which resulted in the function being executed twice and printing “Hello World” two times

A more complex example

Let’s say we have 3 integer variables a, b, c and we want to check if they are even numbers. Without using functions we would write something like this.

a = 3b = 4c = 5if a % 2 == 0:    print('{} is even'.format(a))else:    print('{} is odd'.format(a))if b % 2 == 0:    print('{} is even'.format(b))else:    print('{} is odd'.format(b))if c % 2 == 0:    print('{} is even'.format(c))else:    print('{} is odd'.format(c))>> 3 is odd4 is even5 is odd

You can see it’s relatively long and a lot of code is just repeated for each variable. Now let’s do it using functions.

a = 3b = 4c = 5def is_even(nr):    if nr % 2 == 0:        print('{} is even'.format(nr))    else:        print('{} is odd'.format(nr))is_even(a)is_even(b)is_even(c)>> 3 is odd4 is even5 is odd

The second code block is shorter and we can reuse the function for many other variables by just calling the it again and sending the parameter.

Named parameters

So far we’ve seen functions with parameters that are sent directly. When having several parameters in a function, the order they are transmitted matters.

Let’s consider a function that displays a student’s name and grades.

def display_info(name, grades):    print("Name: {}; Grades: {}".format(name, grades))display_info("John Doe", [10,6,7])>> Name: John Doe; Grades: [10, 6, 7]

The order in which we sent our parameters matters. If we were to reverse the order, we would not get the expected result.

display_info([8,10,8], "Jane Doe")>> Name: [8, 10, 8]; Grades: Jane Doe

We can however use the name of the parameter when calling the function. In this case, the order the parameters is not relevant anymore, since they are now identified by their name instead of the order they are sent in.

display_info(grades=[8,10,8], name="Jane Doe")>> Name: Jane Doe; Grades: [8, 10, 8]

Default values

When defining a function we can specify default values for parameters. If we do, that parameter is optional. The function can be called without that parameter, in which case it will take the default value.

Let’s consider a function that raises one number to a power. The first argument is the number and the second is the power it should be raised at. It will have a default value of 2.

def raise_power(nr, pwr=2):    return pow(nr, pwr)

If we call the function with a single parameter (nr), it will always return its square.

print(raise_power(5))>> 25

If we add the second parameter as well, the function will use its value for the power the number should be at.

print(raise_power(5,4))>> 625

Hopefully this example shows how useful and important functions are. Functions are used all the time and it’s very important to understand how they work and how to call them properly, using named and/or optional parameters.

To learn more about functions check out the two videos below.

Both youtubers have great Python channels. If you are a beginner, I suggest checking out the entire playlists the two videos are part of and consider subscribing to these channels.

Objects

Objects and Object Oriented Programming (OOP) is a huge topic, which can’t possibly be covered in a single article. Like with other concepts, the aim is to explain the basic principle of how they work so we can confidently work with objects provided by libraries we’ll use in the future.

It can be easier to think about objects in programming like abstractions of real-life objects. A car for example, has a number of attributes, such as:

  • Brand
  • Model
  • Color
  • If it’s convertible
  • etc.

Attributes are variables which hold values that describe the object.

A series of actions can be performed with a car, the most basic ones being:

  • Turn engine on/off
  • Accelerate
  • Break
  • etc.

Actions an object can perform are coded through functions inside the object. Such a function is a method.

Values that describe the object are stored in variables inside the object and are its attributes.

Actions the object can perform are coded in functions inside the object and are its methods.

Creating a car object

In Python object are created using the class keyword. Class names should always be capitalized.

Defining an object is like creating a blueprint. With this blueprint we can create specific instances of the class. From the Car blueprint we can create specific cars (Tesla or Honda Civic).

Let’s define our object.

class Car(object):    def __init__(self, brand_att, model_att, color_att):        self.brand = brand_att        self.model = model_att        self.color = color_att

That can look a bit confusing, so let’s take it step by step…

  1. We defined a new object with the name Car, using the class keyword
  2. __init__ is a special method that is always called when a Car object is created. What we want to do in __init__ is place the values we send in object attributes
  3. The __init__ method has the following parameters: brand_att, model_att, color_att.
  4. The parameters’ values are placed in the brand, model and color object attributes. This is done using the self keyword. Self always refers to the object itself.

Let’s create a car using this “blueprint”. Let’s say we want to have a white Tesla Model 3. Just like any other value, we want to store this in a variable so we can access it later.

my_tesla = Car("Tesla", "Model 3", "white")

Now that we have our object, we can access its attributes using dot(“.”)

print(my_tesla.brand)print(my_tesla.model)print(my_tesla.color)>> TeslaModel 3white

Let’s do another one.

my_honda = Car("Honda", "Civic", "red")print(my_honda.brand)print(my_honda.model)print(my_honda.color)>> HondaCivicred

We have two cars and we can access their brand, model and color attributes, but they don’t do that much… It’d be great if we could turn them on and drive a little :)

Creating the first Car object methods

Methods are implementations of what an object “does”. First should be turning and engine on and off. To do this we first need a new attribute which represents the engine status. The class definition must be adjusted.

class Car(object):    def __init__(self, brand_att, model_att, color_att):        self.brand = brand_att        self.model = model_att        self.color = color_att        self.engine_on = False

We added a new attribute engine_on and set it to False. We don’t need a new parameter in the __init__ function.

The turn engine on method should change the engine_on to True if it’s False. Turn engine off method will do the exact opposite. Let’s code the two functions into the object.

class Car(object):    def __init__(self, brand_att, model_att, color_att):        self.brand = brand_att        self.model = model_att        self.color = color_att        self.engine_on = False    def turn_engine_on(self):        if self.engine_on is False:            self.engine_on = True    def turn_engine_off(self):        if self.engine_on is True:            self.engine_on = False

We can access the object’s attributes (and other methods) inside a method definition by using the self keyword. So in the turn_engine_on method we first check if the engine_on attribute is False and if it is, we set it to True. We reverse the process in the turn_engine_off method.

Let’s test the new methods. Since we change the Car object, we need to create our cars again so they include the changes.

print(my_tesla.engine_on)>> Falsemy_tesla.turn_engine_on()print(my_tesla.engine_on)>> Truemy_tesla.turn_engine_off()print(my_tesla.engine_on)>> False

We successfully turned our engine on and off!

Accelerate and Break methods

We can turn the engine on and off, so the next step is to move around a bit. With accelerating and braking methods we could do some drag racing…

First we need a new attribute, speed. We can set that to 0 by default in the __init__ function.

class Car(object):    def __init__(self, brand_att, model_att, color_att, acc_att = 10):        self.brand = brand_att        self.model = model_att        self.color = color_att        self.engine_on = False        self.speed = 0        self.acc = acc_att

To accelerate the car, each time we call the accelerate method we’ll increase the speed by the acc value. We’ll do the reverse for braking and pretend it makes sense…

class Car(object):def __init__(self, brand_att, model_att, color_att, acc_att = 10):        self.brand = brand_att        self.model = model_att        self.color = color_att        self.engine_on = False        self.speed = 0        self.acc = acc_att    def turn_engine_on(self):        if self.engine_on is False:            self.engine_on = True    def turn_engine_off(self):        if self.engine_on is True:            self.engine_on = False    def accelerate(self):        if self.engine_on is True:            self.speed = self.speed + self.acc            print("Speed increased to {}".format(self.speed))    def brake(self):        if self.speed > 0:            self.speed = self.speed - self.acc            print("Speed decreased to {}".format(self.speed))

We instantiate the Tesla again and test our new methods

my_tesla = Car("Tesla", "Model 3", "white")my_tesla.turn_engine_on()my_tesla.accelerate()>> Speed increased to 10my_tesla.accelerate()>> Speed increased to 20my_tesla.accelerate()>> Speed increased to 30my_tesla.brake()>> Speed decreased to 20my_tesla.brake()>> Speed decreased to 10my_tesla.brake()>> Speed decreased to 0my_tesla.turn_engine_off()

We now have a somewhat functioning Car object that is instantiated in the my_tesla variable and a starting understanding of how objects work. Good job indeed!

Inheritance

Inheritance allows the creation of new classes by using old classes. The new class has all the attributes and methods of the old class, plus any new ones that are added to it.

Let’s create a simple Animal class first.

class Animal(object):    def __init__(self):        print("This is an Animal")    def eat(self):        print("I've eaten!")

Great, we’ve established that all animals eat. Now we can create a Dog class and have it bark.

class Dog(Animal):    def __init__(self):        Animal.__init__(self)        print("This is a Dog!")def bark(self):    print("BARK!")

Two things must be noticed. Unlike a regular class, when using inheritance you need to specify the original class name in brackets.

class Dog(Animal)

Second, the original class name __init__ special function must be called in the derived class __init__.

Animal.__init__(self)

Let’s instantiate a Dog and test it.

y_dog = Dog()>> This is an AnimalThis is a Dog!my_dog.bark()>> BARK!

We now have confirmation that the original class must be instantiated first in the derived class __init__.

Inheritance is a technique that allows code reuse.

Polymorphism

Polymorphism is a way for different objects to share the names of methods. Each method has a separate implementation in each object, but the name is the same.

This can be used in certain context to create code code that is easy to read. Let’s see an example of two classes Dog and Cat that both have a method called eat. Since dogs and cats don’t eat the same things, it’s natural that the two differ.

class Dog(object):    def __init__(self):        print("I am a dog")    def eat(self):        print("I eat dog food")class Cat(object):    def __init__(self):        print("I am a cat")    def eat(self):        print("I eat cat food")my_dog = Dog()my_cat = Cat()my_dog.eat()my_cat.eat()
>> I am a dog
I am a catI eat dog foodI eat cat food

Each pet eats the appropriate food. No fights! Both classes have the eat method, each behaving differently.

One advantage of polymorphism is we can easily iterate through different objects and call methods that share name.

for pet in [my_dog, my_cat]:    pet.eat()>> I eat dog foodI eat cat food

We can feed them at the same time :)

To give another example, imagine a list of different geometrical shapes (square, circle, sphere, cube, triangle etc.), iterating through this list and calling the area() method which calculates the area of the shape.

While each a different implementation since each shape has a different area calculation algorithm, the area() method has the same name for all geometric shapes.

For a more in depth look at objects in Python see this series of videos:

Conclusion

In this post we managed the unthinkable, to go over functions AND objects. As always you’ll find the accompanying Jupyter notebook here: https://github.com/clujsoai/python

The final part of this Python Primer series will focus on some useful concepts in Python.

Your opinions, feedback or (constructive) criticism are welcomed in discussions below or at @mariussafta

Join our Facebook and Meetup groups and keep an eye out for discussions and future meetups.

--

--