Inheritance in Python

In an >>earlier posts<< on object-oriented programming in Python, I covered the basics of inheritance. The base class (Superclass) from which the child class inherits all its attributes and methods in the derived class declaration (Subclass) as an argument, i.e.

class Subclass(Superclass):
    pass

When a child class inherits from several base classes, it also inherits attributes and methods from those classes. However, when there are attributes or methods with the same names in two different base classes, the child class inherits this attribute or method whose class was first on the argument list in the child class definition, i.e.

class Person:
    greeting = 'Hi!'
    def function1(self):
        print('hello')

class Employee:
    greeting = "What's up!"
    def function1(self):
        print('world')
    def function2(self):
        pass

class Manager(Employee, Person):
    pass

if __name__ == '__main__':
    john_doe = Manager()
    print(dir(john_doe)
    print(john_doe.greeting)
    john_doe.function1() 

As you can see from the example above, the Manager class has both methods: function1 () and function2 () and the variable greeting. The value of greeting will be What’s up! and the result of the function will be world.

When inheriting a method from a base class, we can override the code of the method (override the method) and then get a completely new functionality, or extend the functionality of the method. The init () method obtained increased functionality in the derived class, while the code of the function1 () method was overridden in the subclass, i.e.

class Person:
    def __init__(self, name):
    	self.name = name
    def function1(self):
    	print('Hello')

class Employee(Person):
    def __init__(self, name, job):
    	super().__init__(name)
    	self.job = job
    def function1(self):
    	print('Hi!')

if __name__ == '__main__':
    john_doe = Employee('John Doe', 'programmer')
    print(john_doe.name)
    print(john_doe.job)
    john_doe.function1()

The function super calls a method from the bass class, in this case init (), so that the method in the subclass uses the code from the base class and adds new functionality.

Instead of using a construction with the function super (), you can use a construction with the name of the base class, i.e.

Person.__init__(self, name)

This is especially useful when a child class inherits from several base classes and we want to use a specific method from a specific base class.

Basics of object-oriented programming in Python #3 instance, class and static methods

As in the case of class attributes and variables, the same division can be observed for methods, i.e. functions included in the class.

Instance methods in the definition as the first argument must have a reference to the class instance – self, e.g.

class Employee:
    bonus = 10
    def set_bonus(self, bonus):
        self.bonus = bonus

The set_bonus method above sets an instance variable that overrides the class variable bonus.

However, when we want to create a class method, we must use the argument cls instead of self, which is a reference to the class and not to an instance. In addition, you must use the @classmethod decorator, i.e.

@classmethod
    def set_base_bonus(cls, bonus):
        cls.bonus = bonus

The above method will set the base value of the variable bonus in the Employee class and all its instances.

Class methods can also be a way to create alternative constructors with a different number of arguments (constructor overloading). For example, considering the example Employee class, which takes the employee’s name and the salary value as arguments, we can create a class method that takes 3 arguments: first name, last name and salary value and returns an instance of the Employee class.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    @classmethod
    def from_full_name(cls, first, last, salary):
        name = '{} {}'.format(first, last)
        return cls(name, salary)

john_doe = Employee.from_full_name('John', 'Doe', 8000)

In addition to class methods, there are also static methods that are preceded by the @staticmethod decorator. Static methods take neither a self instance reference nor cls to a class as an argument. They can take explicit arguments that are parameters to these functions. Static functions are thematically related to the class in which they are contained, but should not operate on class or instance variables.

Basics of object-oriented programming in Python #2 – init() method, instance and class variables

When an instance of a class is created, a special init () method is executed which can be equated with a constructor from other languages. It has the form:

def __init__(self, arg1, arg2, ... ):
    pass

The first argument is self by default, which is a reference to the current class instance. The remaining arguments are optional and, like in constructors from other languages, they are used to set attribute values. Instance attribute names must also be preceded by the self variable.

In addition to instance variables, there are also class variables. They hold values ​​that are the same for all instances of the class. It is also possible to display the values ​​of these variables without creating an instance of the class, just by specifying the class name, e.g. for the Employee class, we create a variable that stores the percentage value of the salary bonus that each employee receives, i.e.

class Employee:
    bonus = 10
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
john_doe = Employee('John Doe', 8000)    # creating an instance of the class
print(john_doe.name)    # displays the value of the instance  attribute name
print(john_doe.bonus)   # displays the value of a class variable from an instance
print(Employee.bonus)   # displays the value of a class variable

A class variable can hold, for example, the number of all employees of the enterprise. It has the same value whether it is called from the class or any instances of it. The increment of the class variable storing the number of employees can be placed e.g. in the init () method, then creating a new employee increases the value of the class variable.

However, you can create an instance variable with the same name as the class variable. In this case, the class variable will be overridden by the value of the attribute, i.e.

class Employee:
    bonus = 10
    def set_bonus(self, bonus):
        self.bonus = bonus

john_doe = Employee()
print(john_doe.bonus)   # displays the value 10
john_doe.set_bonus(20)  # setting a bonus value
print(john_doe.bonus)   # displays the value 20 - the employee bonus
print(Employee.bonus)   # displays the value 10 - base bonus  

>>part three<<

Basics of object-oriented programming in Python #1

Take a class named Person as an example. According to the convention, the name of the class should start with a capital letter, and in the case of a name consisting of many words, we use Pascal case – all words written together and beginning with capital letters. The basic definition of a class is as follows:

class Person:
    pass

All classes inherit from the base object class by default, so you could define the class as follows:

class Person(object):
    pass

Objects can be created from a class that contains only an empty pass statement. Creating an object does not require a special keyword, e.g. new

 john_doe = Person()   # creating an instance of Person class

print(john_doe)   # showing name of an instance and its address

print(dir(john_doe))   # showing all available class methods

To define a class that inherits from another class, put the base class name as an argument, i.e .:

class Employee(Person):
    pass

Python supports multi-inheritance, i.e. you can create a class that inherits from multiple base classes, e.g.

class Executive(Employee, Manager):
    pass 

>>part two<<

Simple console calendar #2 (raising errors, sys.argv values handling)

In the earlier >>article<< the console calendar does not use the richness of the calendar module. To simplify the code, you can use the ‘ready-made’ class TextCalendar from this module.

You can define the cal2 () function, which can be imported from the cal2 module, or called from the console. When invoked from the console, you need to give the file execute permissions (chmod + x cal2.py).

Running the file without any arguments, ie ./cal2.py, will display the calendar for the current year and month in the console. Alternatively, we can run the program with 2 arguments: year and month. Specifying a different number of arguments will cause a TypeError.

#!/usr/bin/env python

import calendar

from datetime import datetime

import sys

def cal2(year=datetime.today().year, month=datetime.today().month):
    '''
    Simple console calendar.
    '''
    c = calendar
    text_calendar = c.TextCalendar(c.firstweekday())
    print(text_calendar.formatmonth(year, month))

if __name__ == '__main__':
    args = sys.argv[1:]
    if len(args) == 0:
        cal2()
    elif len(args) == 2:
        cal2(int(args[0]), int(args[1]))
    else:
        raise TypeError(
            'cal2() takes 2 arguments ({} given)'.format(len(args)))

Instead of the TextCalendar class, we can use a child class – LocaleTextCalendar and get subtitles in accordance with the local language settings.

Accessors and mutators in Python

The concept of object-oriented programming is related to the concept of encapsulation, i.e. hiding class fields and making only public methods (public API) available to read or change the values ​​of these fields. In Java, for example, it is solved at the language level using the private access modifier.

Python does not support such encapsulation. Using private variables like: _variable is just a matter of convention.

Consider the following example where we create a class to represent an employee:

class Employee:
	def __init__(self, name, salary, phone):
	    self.name = name
	    self.salary = salary
	    self.phone = phone

In Java, you should declare the name, salary, and phone variables private and refer to them via the getters and setters . In Python, it is not recommended to write accessor whose sole purpose is to return an attribute value or mutator to set an attribute value. Instead, you can refer to these fields directly. This has many advantages, but the advantages of encapsulating e.g.

johnDoe = Employee('John Doe', 8000, '555 111 22 33')
johnDoe.salary = 9000
print(johnDoe.salary)

The use of direct access to class attributes is a problem when it is necessary to make changes, e.g. introducing correctness control of the entered data.

Based on the code of the previously created Employee class, let’s assume that we would like to validate the entered telephone number, i.e. whether it has the appropriate number of digits, and at the same time remove unnecessary characters that are not digits.

To do this, we don’t need to change the way we communicate with the phone variable. We can still refer directly to the variable name. This is possible thanks to the @property decorator.

class Employee:

    def __init__(self, name, salary, phone):
        self.name = name
        self.salary = salary
        self.phone = phone
    @property
        def phone(self):
           return self._phone
    @phone.setter
        def phone(self, value):
            phone = ''.join(filter(lambda x: x.isdigit(), value))
            if len(phone) != 10:
                raise ValueError('Wrong phone number!')
            self._phone = phone

Calling the class’s constructor sets individual class attributes, while assigning the phone argument to the self.phone attribute causes the mutator to be called and thus validating the entered phone number at the time of creating the Employee class object.

Similarly, assigning a new value to the phone attribute will call the appropriate validation method, i.e.

johnDoe = Employee('John Doe', 8000, "555 111 22 33")
johnDoe.phone = '000' # raises ValueError - incorrect number length

Abiword with ODT file support

Abiword is an interesting alternative to LibreOffice. Of course, it has fewer functions and is not a full 1: 1 equivalent, but it also has undeniable advantages, e.g. speed of operation, installation of the word processor itself.

There is a current version of the program in the extra Arch Linux repository which uses gtk3. Unfortunately, at least for me (ArchLabs) it does not work well, i.e. there is a flickering of the program’s working area.

There is an alternative version of the program in AUR repositories – abiword-gtk2, which of course uses gtk2. The flickering problem is gone, but this version is lite version and there is no support for e.g. plugins, including support for LibreOffice odt files.

To build a version with support for odt files, delete the entries in the PKBUILD file:

--disable-builtin-plugins \
--disable-default-plugins \

Instead of these, put an entry:

--enable-plugins="opendocument openwriter openoffice writer filter" \

You can generate a package:

makepkg -Acs

And then install:

sudo pacman -U abiword-gtk2-replace _with _program_version.tar.xz

and there you go!

Basics of Exception Handling in Python #3

It is also worth mentioning the optional else procedure, which is executed only when there is no exception in the related try , i.e.

  • if an else procedure is present, it must follow except procedure
  • there can be only one else procedure (or none)

Use the else proc. if you don’t want a certain piece of code to be contained in a try part. If you put all the code in a try procedure instead, the cause of the exception would be potentially more difficult to locate.

Another optional procedure is a finally proc., which executes whether or not there is an exception:

  • if there is a finally proc. it must be placed after except and else (if present).
  • there can be only one finally proc. (or none)

The finally block is mainly used for releasing access to external resources eg. databases.

Basics of Exception Handling in Python #2

Of course, we can also use the syntax in which the exception description is assigned to a variable and we can then display it in the handling procedure, e.g.

except NameError as e:
    print('Message:', e)

You can create a common handler for several exceptions by putting class names in a tuple, e.g.

except (ZeroDivisionError, NameError):
    # Here we put the handler for these two exceptions

What you should pay attention to is that exceptions that are child classes in the hierarchy of exception classes are handled before the classes from which they inherit. The wrong order will result in the exception being handled by the more general procedure that came first, i.e.

except Exception:
    print('It catches all exceptions!')
except NameError:
    print('NameError exception')

In the above example, NameError will be handled by the procedure in the first except block. Despite the NameError exception, the following message will be displayed: It catches all exceptions! and the routine that specifically handles this exception (and displays NameError exception in this case) will not be executed.

>>part three<<

Basics of Exception Handling in Python #1

Errors that show up during the execution of the program and are not syntax errors belong to the so-called exceptions. The list of exceptions is quite extensive >>see<<

We can handle these exceptions by putting code that can cause an exception in a try block, i.e.

ry:
    # Here we put code that can raise an exception or exceptions

and the code to handle the exception in the except block (equivalent to Java catch), i.e.

except:
    # Here we put the exception handling code

It is more useful to react to specific exceptions than to handle all of them “in bulk”. The except statement itself without specifying the name of the error class can be useful if you want to catch an exception that has not yet been handled by an earlier except block, e.g.

except ZeroDivisionError:
    # Here we put the code to handle the ZeroDivisionError exception

except NameError:
    # Here we put the code to handle the NameError exception

except:
    # Here we catch the remaining unhandled exceptions

>>part 2<<