Object-Oriented Programming

Python is an object-oriented programming language. The key notion is that of an object. An object consists of two things: data and functions (called methods) that work with that data. As an example, strings in Python are objects. The data of the string object is the actual characters that make up that string. The methods are things like lower, replace, and split. In Python, everything is an object. That includes not only strings and lists, but also integers, floats, and even functions themselves.

Creating your own classes.

A class is a template for objects. It contains the code for all the object’s methods.

Here is a simple example to demonstrate what a class looks like. It does not
do anything interesting.

class Esempio:
    def __init__(self, a,b):
        self.a = a
        self.b = b
    
    def add(self):
        return self.a + self.b

e = Esempio(8,6)
print(e.add())


To create a class, we use the class statement. Class names usually start with a capital.
Most classes will have a method called __init__. The underscores indicate that it is a special kind of method. It is called a constructor, and it is automatically called when someone creates a new object from your class. The constructor is usually used to set up the class’s variables. In the above program, the constructor takes two values, a and b, and assigns the class variables a and b to those values.
The first argument to every method in your class is a special variable called self. Every time your class refers to one of its variables or methods, it must precede them by self. The purpose of self is to distinguish your class’s variables and methods from other variables and functions in the program.
To create a new object from the class, you call the class name along with any values that you want to send to the constructor. You will usually want to assign it to a variable name. This is what the line e=Esempio(8,6) does.
To use the object’s methods, use the dot operator, as in e.add().

Here is a class called Analyzer that performs some simple analysis on a string. There are methods to return how many words are in the string, how many are of a given length, and how many start with a given string.

from string import punctuation

class Analizza:
    def __init__(self, s):
        for c in punctuation:
            s = s.replace(c,'')
            s = s.lower()
            self.parole = s.split()
    
    def numeri_di_parole(self):
        return len(self.parole)
    
    def inizia_con(self,s):
        return len([w for w in self.parole if w[:len(s)]==s])
    
    def numero_di_lunghezza(self, n):
        return len([w for w in self.parole if len(w)==n])


s = 'Tanto va la gatta al lardo che ci lascia lo zampino'

analizzatore = Analizza(s)

print(analizzatore.parole)
print('Numero di lettere:', analizzatore.numeri_di_parole())
print('Numero di parole che iniziano con la "c":', analizzatore.inizia_con('c'))
print('Numero di parole formato da due lettere:', analizzatore.numero_di_lunghezza(2))


A few notes about this program:

  • One reason why we would wrap this code up in a class is we can then use it a variety of different programs. It is also good just for organizing things. If all our program is doing is just analyzing some strings, then there’s not too much of a point of writing a class, but if this were to be a part of a larger program, then using a class provides a nice way to separate the Analizza code from the rest of the code. It also means that if we were to change the internals of the Analizza class, the rest of the program would not be affected as long as the interface, the way the rest of the program interacts with the class, does not change. Also, the Analizza class can be imported as-is in other programs.
  • The following line accesses a class variable: print(analizzatore.parole).

You can also change class variables. This is not always a good thing. In some cases this is convenient, but you have to be careful with it. Indiscriminate use of class variables goes against the idea of encapsulation and can lead to programming errors that are hard to fix. Some other object-oriented programming languages have a notion of public and private variables, public variables being those that anyone can access and change, and private variables being only accessible to methods within the class. In Python all variables are public, and it is up to the programmer to be responsible with them. There is a convention where you name those variables that you want to be private with a starting underscore, like _var1. This serves to let others know that this variable is internal to the class and shouldn’t be touched.

Inheritance

In object-oriented programming there is a concept called inheritance where you can create a class that builds off of another class. When you do this, the new class gets all of the variables and methods of the class it is inheriting from (called the base class). It can then define additional variables and methods that are not present in the base class, and it can also override some of the methods of the base class. That is, it can rewrite them to suit its own purposes. Here is a simple example:

class Parent:
    
    def __init__(self, a):
        self.a = a
    
    def method1(self):
        return self.a*2
    
    def method2(self):
        return self.a+'!!!'

class Child(Parent):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
    def method1(self):
        return self.a*7
    
    def method3(self):
        return self.a + self.b

p = Parent('hi')
c = Child('hi', 'bye')

print('Parent method 1: ', p.method1())
print('Parent method 2: ', p.method2())
print()
print('Child method 1: ', c.method1())
print('Child method 2: ', c.method2())
print('Child method 3: ', c.method3())


We see in the example above that the child has overridden the parent’s method1, causing it to now repeat the string seven times. The child has inherited the parent’s method2, so it can use it without having to define it. The child also adds some features to the parent class, namely a new variable b and a new method, method3.

A note about syntax: when inheriting from a class, you indicate the parent class in parentheses in the class statement.

If the child class adds some new variables, it can call the parent class’s constructor as demonstrated below. Another use is if the child class just wants to add on to one of the parent’s methods. In the example below, the child’s print_var method calls the parent’s print_var method and adds an additional line.

class Parent:
    def __init__(self, a):
        self.a = a
    
    def print_var(self):
        print("The value of this class's variables are:")
        print(self.a)

class Child(Parent):
    def __init__(self, a, b):
        Parent.__init__(self, a)
        self.b = b

def print_var(self):
    Parent.print_var(self)
    print(self.b)

Note – You can also inherit from Python built-in types, like strings (str) and lists (list), as well as any classes defined in the various modules that come with Python.
Note – Your code can inherit from more than one class at a time, though this can be a little tricky.

Rispondi