DEV Community

Oluchi Orji
Oluchi Orji

Posted on • Updated on

A GENTLE INTRODUCTION TO PYTHON CLASSES FOR NEWBIES

Python is an object-oriented programming language (OOP), it is a very popular programming paradigm and is one of the most effective approaches to writing software.

Almost everything in Python is an object, with its properties and methods. So when solving real-world challenges, you create objects based on classes which are also known as instantiation(instances).

Class is defined as an object's blueprint or description and it is created with the keyword class.

Let me illustrate what a class looks like.

Create a class named Cake, so what do you know about cakes, you know we use flour to make the dough and an oven to heat the dough.
So we create an attribute named flour and oven.
When we want to bake a cake, we preheat the oven and mix our flour with other ingredients, so we create a method called preheat_oven and mix_dough.

class Cake:
    def __init__(self, flour, oven):        
        """Initialize attributes."""
        self.flour = flour
        self.oven = oven

    def preheat_oven(self):
        """create a method"""
        return f"Pre-heat {self.oven} to 350 degrees"
    def mix_dough(self):
        return f" Mix together {self.flour}, cocoa powder, baking powder" 

Now that our Cake class is created, we would create an object(instance) of the class called pound_cake.

pound_cake = Cake()

There’s a lot I have done here, but don’t worry I will break it down with the post.

1. The init() Method

All classes have a function called init(), which is always executed when we create a new instance based on the class and it is used to assign values to object properties, as the example above, we used init () function to assign values for oven and flour.
Whenever we want to make an instance from the Pizza class, we’ll provide values for only the last two parameters flour and oven.

2. The self Parameter

The two variables defined (oven and flour) each have the prefix self.
i. Any variable prefixed with self indicates the variable is available to every method in the class.
ii. It is used to access variables that belong to the class.

3. Methods

Classes can have methods defined in order to add functionalities.
Methods are functions that belong to the object.
Remember all methods have self as their first parameter

def preheat_oven(self):
    pass

4. Accessing attributes and calling methods.

To access the attributes of an instance, you use dot notation.
We created an instance of the Pizza class called pound_cake, so in order to access attribute flour, we do the following.

pound_cake = Cake("wheat flour","oven")
print(pound_cake.flour)
wheat flour

After we created an instance from the class Cake, we can use dot notation to call any method defined in Cake.

print(pound_cake.preheat_oven())
print (pound_cake.mix_dough())

#We can also create multiple instances too
sponge_cake =Cake('pastry flour', 'Roaster oven')
chiffon_cake = Cake ('whole wheat flour', 'Roaster oven')
print(chiffon_cake.mix_dough())

Pre-heat oven to 350 degrees
 Mix together wheat flour, cocoa powder, baking powder
 Mix together whole wheat flour, cocoa powder, baking powder

5. Modifying an Attribute’s Value through a Method

It can be helpful to have methods that update certain attributes for you. Instead of accessing the attribute directly, you pass the new value to a method that handles the updating internally.

Write a class that allows users to choose their favorite pizza toppings and favorite crust type from a pizza app.
Now the store is running a promo when you order a Margherita pizza, you can order a free drink.

Modify the drink attribute through the order_margherita method

class Pizza:
    def __init__(self, veggies, toppings, crust, oil):
        self.veggies = veggies
        self.toppings = toppings
        self.crust = crust
        self.oil = oil

    def order_margherita(self, drink):
        """modify attribute value here"""
        self.drink = drink
        return f"You ordered for {self.crust} pizza with the {self.veggies},{self.toppings},{self.oil} toppings and you have gotten a free {drink}"



margherita_pizza = Pizza( "tomatoes", "mozzarella" ,"thick" ,"extra virgin olive oil.")
print(margherita_pizza.order_margherita("coke"))

You ordered for thick pizza with the tomatoes, mozzarella, extra virgin olive oil. toppings and you have gotten a free coke.

6. Assigning a Default Value for an Attribute

When you assign a default attribute value, you don’t have to include a parameter for that attribute.

Still using our Pizza class, let me assign a default value for meat attribute and diary.

class Pizza:
    def __init__(self, veggies, toppings, crust, oil):
        self.veggies = veggies
        self.toppings = toppings
        self.crust = crust
        self.oil = oil
        self.meat = "beef"
        self.dairy = "cheese"

    def order_margherita(self, drink):
        """modify attribute value here"""
        self.drink = drink
        return f"You ordered for {self.crust} pizza with the {self.veggies},{self.toppings},{self.oil} toppings and you have gotten a free {drink}"

    def order_pepperoni(self):
        return f"You ordered {self.crust} pizza topped with {self.meat} and {self.dairy}"

order_pepperoni = Pizza( "chilli","oregano," ,"thin" ,"extra virgin olive oil.")
print(order_pepperoni.order_pepperoni())
You ordered thin pizza topped with beef and cheese

INHERITANCE

Inheritance allows us to define a class that inherits all the methods and properties of another class.

The parent class is the class being inherited from, also called a base class.
The child class is the class that inherits from another class.

The syntax looks like this

class ChildClass(ParentClass):
    pass

Create a child class named OrderPizza, which will inherit the properties and methods from the Pizza class

class OrderPizza(Pizza):
   pass

order_pizza = OrderPizza( "chilli","oregano","thin" ,"soya bean oil.") #create an instance
print(order_pizza.order_pepperoni())   #call a method

You ordered thin pizza topped with beef and cheese

Adding init() method and super() to a Child Class.

i. When you add the init() function, the child class will no longer inherit the parent's init() function,in order
to keep the inheritance of the parent's init() function, add a call to the parent's init() function:

class OrderPizza(Pizza):
    def __init__(self, veggies, toppings, crust, oil):
        Pizza.__init__(self, veggies, toppings, crust, oil)

pizza = OrderPizza( "tomatoes", "oregano" ,"thin","extra virgin olive oil.")
print(pizza.order_margherita("coca-cola"))

You ordered for thin pizza with the tomatoes, oregano, extra virgin olive oil. toppings and you have gotten a free coca-cola

ii. Python also has a super() function that will make the child class inherit all the methods and properties from its parent.

class OrderPizza(Pizza):
    def __init__(self, veggies, toppings, crust, oil):
        super().__init__(veggies, toppings, crust, oil)
pizza = OrderPizza( "tomatoes", "oregano" ,"thin","extra virgin olive oil.")
print(pizza.order_margherita("coca-cola"))
You ordered for thin pizza with the tomatoes,oregano,extra virgin olive oil. toppings and you have gotten a free coca-cola

Adding Attributes and Methods for the Child Class

Let’s add an attribute that allows the user to choose the delivery type and a method to order their pizza delivered.

class OrderPizza(Pizza):
    def __init__(self, veggies, toppings, crust, oil):
        Pizza.__init__(self, veggies, toppings, crust, oil)
        self.delivery="Bike man"
    def ride_confirmation(self):
        return f"A {self.delivery} will deliver your pizza in twenty minutes"

pizza = OrderPizza( "tomatoes", "oregano" ,"thin","soya oil")
print(pizza.ride_confirmation())
A Bike man will deliver your pizza in twenty minutes

CLASS AND STATIC METHODS

Class Methods

What we have looked at so far are self.attributes and methods.
The @classmethod decorator is a function decorator that instantiates an instance of a class using a different parameter(s) than those usually passed to the class constructor.
i. They are usually marked with a classmethod decorator and takes an implicit first argument ( cls )
ii. It doesn't return the instance of a class but a new object of the class cls.

An example below, maybe the Pizza startup wants to serve clients that are watching their sodium intake.

class OrderPizza(Pizza):
    def __init__(self, veggies, toppings, crust, oil):
        Pizza.__init__(self, veggies, toppings, crust, oil)
        self.delivery="Bike man"
    def ride_confirmation(self):
        return f"A {self.delivery} will deliver your pizza in twenty minutes"

    @classmethod
    def healthier_food(cls, vegetable):
        return f"Make me a {vegetable} salad"

order_salad = OrderPizza.healthier_food("parsley")
print(order_salad)
Make me a parsley salad

Static Methods

Static methods are similar to class methods except that they don't receive an implicit first argument.

i. They are usually marked with staticmethod decorator
ii. They behave like plain functions except that you can call them from the instance of the class.

An example below, maybe the Pizza store is out of bacon, it alerts the user to select another topping.

class OrderPizza(Pizza):
    def __init__(self, veggies, toppings, crust, oil):
        Pizza.__init__(self, veggies, toppings, crust, oil)
        self.delivery="Bike man"
    def ride_confirmation(self):
        return f"A {self.delivery} will deliver your pizza in twenty minutes"

    @classmethod
    def healthier_food(cls, vegetable):
        return f"Make me a {vegetable} salad"

    @staticmethod
    def order_bacon_pizza(topping):
        if topping == "bacon":
            return f"we don't have bacon in the store now, please choose another"
        else:
            return f"your pizza with {topping} is ready"

toppings = ["cabbage", "bacon","beef"]
for topping in toppings:
    pizza = OrderPizza.order_bacon_pizza(topping)
    print(pizza)
your pizza with cabbage is ready
we don't have bacon in the store now, please choose another
your pizza with beef is ready

YAY! 🙌 🎉🎉 We are almost done, let us test our Pizza class (you can test for the child class later).
The key to developing quality software is to test your code as you write it.

Create a new file (test.py) and add the following code below.

import unittest
from pizza import Pizza


class TestPizza(unittest.TestCase):
    """Tests for the Pizza class"""
    def test_make_marinara(self):
        pizza = Pizza("tomatoes", "oregano", "thin", "coconut oil.")
        pizza = pizza.make_marinara()
        self.assertIn('oregano', pizza)
        self.assertEqual(
            'You ordered for thin pizza with the following toppings tomatoes,oregano,coconut oil.',
            pizza)
        self.assertNotEqual("order a pizza", pizza)
        self.assertNotIn("cheese", pizza)

    def test_order_pepperoni(self):
        pizza = Pizza("tomatoes", "oregano", "thin", "coconut oil.")
        pizza = pizza.order_pepperoni()
        self.assertEqual('You ordered thin pizza topped with beef and cheese',
                         pizza)
        self.assertNotEqual("order a pepperoni pizza", pizza)


unittest.main()

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

I really hope you liked the article, please feel free to share your opinions about it, hopefully, I hope to recreate this in JavaScript.

References

  1. Python Crash Course by Eric Mathes
  2. Solo learn
  3. W3school

Let us connect Twitter | LinkedIn

Top comments (8)

Collapse
 
reritom profile image
Tomas Sheers

As a note, I'd say that the examples used could use some small adjustments.

A cake class wouldn't really have a method for preheating the oven. Same with pizzas not having methods for ordering pizzas.

A child class of a pizza would more likely be type of pizza like Margherita(Pizza). And then you could have a class like PizzaShop with the method order_pizza().

OrderPizza itself seems more like a function or a method of another class, than a class itself.

Collapse
 
oluchi profile image
Oluchi Orji

Thanks for your feedback, anyways all these are simple illustrations to help newbies come up with speed, anyways there is always a room for adjustment.

Collapse
 
sab6 profile image
Shereef Bankole

Many thanks for your time and the great effort in writing a step by step guide on how to write classes and build test cases in python. One of the beauties of object-oriented programming is that real-world things can be modeled with it. As many of the readers of the articles, I have one of two takeaways from the article. However, some of my comments are highlighted below.

  1. Just for emphasis, newbies should be aware that by convention, each first letter in which a class is composed of is capitalized (for some reasons) e.g. Cake and Pizza and OrderPizza (note that Order and Pizza are two separate words, but because you can’t have space in between the two in python, both were combined into OrderPizza). It is not advisable to use underscore (_) in naming a class.

  2. The dash before and after the _init_() method are four underscores i.e. two before and two after init word.

  3. The class Cake was used to introduce how to create classes, but in the description of the _init_() method Pizza class which came latter was referred to (I guess it’s an oversight).

  4. In the test case, make_marinara() method was tested, I know it would be part of the module imported during the testing; however, it wasn’t shown as part of the methods associated with the class Pizza. Some that are following the codes won't be aware of this.

  5. The order_margherita method associated with the Pizza class is not modifying any attributes. If an initial value had been set for drink in the _init_ () method, one can modify or update the value with a method or directly.

I think the code might look like this:

Class Pizza:

def _init_(self, veggies, toppings, crust, oil):

self.veggies = veggies

self.toppings = veggies

self.crust = crust

self.oil = oil

self.drink = 'fanta' #attribute with a default value needn’t be included as part of the #parameters in the _init_() method

def order_margherita(self):

return f”…

def modify_drink(self):

self.drink = ''

your_order =Pizza(veggies, toppings, crust, oil) #create an instance your_order

your_order = your_order.modify_drink(‘coke)

print(your_order. order_margherita())

Collapse
 
oluchi profile image
Oluchi Orji

Good catch!I will update the article pretty soon!

Collapse
 
praiselordson profile image
Lordson Praise

Thanks Ma for the program. It's very homely and interesting to learn. Regards please

Collapse
 
chigo4404 profile image
chigo4404

Thanks for your effort oluchi.

Collapse
 
oluchi profile image
Oluchi Orji

You are welcome 😊

Collapse
 
wadudu25 profile image
kingkutubu

Thank you Madam, This article has been very helpful.