Can we learn Python without OOPs concepts

Python course

Previous chapter: exception handling
Next chapter: slots

Classes

Preliminary remark

We have completely revised and expanded this chapter for Python3. We recommend that you work through the following chapters there: Since the maintenance and expansion of four different Python tutorials - i.e. Python2 in German and English and also Python3 in both languages ​​- means an enormous amount of work, we have decided to focus mainly on the future focus on German and English tutorials for Python3. We hope for your understanding!

Object-oriented programming (OOP)

Object-oriented programming (OOP for short) has enjoyed great popularity since its "introduction" or "invention" with "Simula 67" by Ole-Johan Dahl and Kristen Nygard. However, it is not undisputed. For example, the Russian computer scientist Alexander Stepanow certified the OOP with only a limited mathematical perspective and said that the OOP was almost as big a fraud as artificial intelligence.1 Alexander Stepanow played a key role in the development of the "C ++ Standard Template Library", so he should be very familiar with OOP and its problems in practice.
The basic concept of object-oriented programming is to summarize data and their functions (methods) - i.e. functions that can be applied to this data - in one object and to encapsulate them externally so that methods of external objects cannot manipulate this data directly.
Objects are defined using classes.
A class is a formal description of what an object is like, i.e. which attributes and which methods it has.
A class must not be confused with an object. Instead of an object, one speaks of an instance of a class.

Analogy: cake class

When introducing object-oriented programming, examples from everyday life are often used. These are mostly examples that help to clarify object-oriented concepts, but these examples cannot then be converted into program code. So also in this example of a cake class.
Let's consider the recipe of a strawberry cake. Then you can consider this recipe as a class. That is, the recipe determines how an instance of the class must be designed. If someone bakes a cake according to this class, then he creates an instance or an object of this class. There are then various methods of processing or modifying this cake. By the way, a nice method is "eat up" in this example.
A strawberry cake belongs to a superordinate class "cake", which inherits its properties, e.g. that a cake can be used as a dessert, to subclasses such as strawberry cakes, sponge cakes, pies and so on.

Objects

The central term in object-oriented programming is that of the object. In OOP, an object denotes the mapping of a real object with its properties and behavior (methods) in a program. In other words: An object can always be described by two things:

  • what it can do or what we can do with it in a program
  • what we know about it
Objects are instances or instances of a class. The terms object and instance are mostly used synonymously and denote the same "object". Objects or instances are created using constructors. Constructors are special methods for creating instances of a class. The destructor method is used to remove or delete instances.

class

A class is an abstract generic term for the description of the common structure and common behavior of real objects (classification).
Real objects are abstracted to the features that are important for the software.
The class serves as a blueprint for mapping real objects into software objects, the so-called instances. The class combines the properties (attributes) required for this and the methods required for manipulating the properties.
Classes are often related to one another. For example, you have an upper class (cake) and another class is derived from this (strawberry cake). This derived class inherits certain properties and methods of the superclass.
Methods and properties using the example of the "Account holder" and "Account" classes:


Encapsulation of data

Another major advantage of OOP is the encapsulation of data.
Properties can only be accessed using access methods. These methods can contain plausibility tests and they (or "only" they) have "information" about the actual implementation.
For example, a method for setting the date of birth can check whether the date is correct and within a certain range, e.g. current account for children under 14 not possible or customers over 100 years unlikely.

Inheritance

In our example it is easy to see that an "Account" class cannot satisfy a real bank.
There are different types of accounts: current account, savings account, etc.
But all the different accounts have certain properties and methods in common. For example, each account will have an account number, an account holder, and an account balance. Common Methods: Depositing and Withdrawing
So there is such a thing as a basic account from which all other accounts "inherit".

Inheritance is therefore used to create new classes based on existing classes. A new class can arise both as an extension and as a restriction of the original class.

The simplest class

The definition of a new class in Python starts with the keyword class.

class account (object): pass The above class has neither attributes nor methods. Incidentally, the "pass" is an instruction that tells the interpreter that the actual instructions will not be "delivered" later.
An object or an instance of the above (empty) class is created as follows: >>> Account () <__main__.konto object="" at="" 0x7f5feca55750="">

Definition of methods

Outwardly, a method only differs from a function in two respects:

  • It is a function that is defined within a class definition.
  • The first parameter of a method is always a reference self to the instance from which it is called.
The self parameter only appears when defining a method. It is not specified when it is called.
Example with method:
class account (object): def transfer (self, target, amount): pass def deposit (self, amount): pass def pay out (self, amount): pass def account balance (self): pass

Constructor

In fact, there are no explicit constructors or destructors in Python. The __init__ method is often referred to as a constructor. If it were really a constructor, it would likely be called __constr__ or __constructor__. Instead it is called __init__, because this method is used to initialize an object that has previously been automatically generated ("constructed"). This method is called immediately after the construction of an object. So it looks as if the object was created by __init__. This explains the frequently made mistake in the designation.

We now use the __init__ method to initialize the objects in our account class. __init__
Constructors are defined like other methods:

def __init __ (self, owner, account number, account balance, current account = 0): self.Inhaber = owner self.Account number = account number self.Account balance = account balance self.Current account = current account

Destructor

The same applies to what has already been said under the Constructors section: There are no explicit constructors. A method __del__ can be defined for a class. If you delete an instance of a class with del, the __del__ method is called. However, only if there is no further reference to this instance. Destructors are principally required in C ++ because they are used for garbage collection. Since you don't have to worry about garbage collection in Python, the __del__ method is used relatively rarely.
The following is an example with __init__ ("constructor") and __del__ (destructor):

class Greeting: def __init __ (self, name): self.name = name def __del __ (self): print "Destructor started" def SayHello (self): print "Hello", self.name This class is now used interactively:> >> execfile ("hello_class.py") Good afternoon Guido >>> x1 = Greeting ("Guido") >>> x2 = x1 >>> del x1 >>> del x2 Destructor started
One should, however, be careful when using the __del__ method. "del x" only starts the __del method when there are no further references to the object, i.e. when the reference counter is set to 0. Problems arise, for example, when there are circular references (circular references), such as with doubly linked lists.

Full example of account class

class Account (object): def __init __ (self, owner, account number, account balance, current account = 0): self.Inhalter = owner self.Account number = account number self.Account balance = account balance self.Contact account = transfer current account def (self, target, amount ): if (self.Account balance - amount < -self.kontokorrent):="" #="" deckung="" nicht="" genuegend="" return="" false="" else:="" self.kontostand="" -="betrag" ziel.kontostand="" +="betrag" return="" true="" def="" einzahlen(self,="" betrag):="" self.kontostand="" +="betrag" def="" auszahlen(self,="" betrag):="" self.kontostand="" -="betrag" def="" kontostand(self):="" return="" self.kontostand="" hat="" man="" dieses="" beispiel="" unter="" konto.py="" abgespeichert,="" kann="" man="" in="" einer="" python-shell="" wie="" folgt="" damit="" arbeiten:="">>> from account import account >>> K1 = account ("Jens", 70711,2022.17) >>> K2 = account ("Uta", 70813,879.09) >>> K1.account balance () 2022.17 >>> K1. transfer (K2,998.32) True >>> K1.account balance () 1023.85 >>> K2.account balance () 1877.41

"Public" blemish

The Account () class still has one small flaw. The attributes can be accessed directly from the outside:

>>> K2.account balance 1877.4100000000001 >>> K2.account number 70813 >>> K2.current account 0 >>> K2.account balance = 1000000 >>> K2.account balance 1000000

Data encapsulation

Normally, all attributes of a class instance are public, i.e. accessible from the outside. Python provides a mechanism to prevent this from happening. The control does not take place via any special keywords but via the names, i.e. a single underscore before the actual name for the protected and double underscore for private, as can be seen in the following table:

Names
description
importance
SurnamePublic
Attributes without leading underscores can be read and written both within a class and from outside.
_SurnameProtected
You can also read and write from the outside, but the developer makes it clear that you shouldn't use these members.
__SurnamePrivate
Are not visible from the outside and cannot be used.
Our example account class with private members looks like this: class Account (object): def __init __ (self, owner, account number, account balance, current account = 0): self .__ owner = owner self .__ account number = account number self .__ account balance = account balance self .__ current account = current account def transfer (self, target, amount): if (self .__ account balance - amount < -self.__kontokorrent):="" #="" deckung="" nicht="" genuegend="" return="" false="" else:="" self.__kontostand="" -="betrag" ziel.__kontostand="" +="betrag" return="" true="" def="" einzahlen(self,="" betrag):="" self.__kontostand="" +="betrag" def="" auszahlen(self,="" betrag):="" self.__kontostand="" -="betrag" def="" kontostand(self):="" return="" self.__kontostand="">

Static members

Up until now, each object in a class had its own attributes and methods that differed from those of other objects.
This is known as "non-static" or dynamic because they are dynamically created for each object in a class.
But how can you, for example, count the number of different instances / objects of a class? In our account () class, this is the number of different accounts.
Static attributes are defined outside of the constructor directly in the class block. It is customary to position the static members directly below the class statement.

In our example of the account class, for example, the number of accounts within the program can only be counted statically:

class account (object): Objekt_zaehler = 0 def __init __ (self, owner, account number, account balance, current account = 0): self .__ owner = owner self .__ account number = account number self .__ account balance = account balance self .__ current account = current account.object_counter + = 1 def __del __ (self): account.object_counter - = 1 In the following interactive session we can see how this counting is done: >>> execfile ("account.py") >>> k1 = account ("Homer Simpson ", 2893002, 2325.21) >>> Account.object_counter 1 >>> k2 = Account (" Fred Flintstone ", 2894117, 755.32) >>> k3 = Account (" Bill Gates ", 2895007, 5234.32) >>> Account. object_counter 3 >>> k2.object_counter 3 >>> del k3 >>> account.object_counter 2

Inheritance

Just as the instances are counted in the Account () class, it could also be necessary or useful in other classes. But you don't want to transfer the code for counting up into the constructor and that for counting down into the destructor in every class.
There is the possibility of passing on the ability to count instances to other classes.
To do this, you define an "upper" class counter that transfers its properties to others, such as an account. In the diagram opposite, we assume, for example, that the classes "Account", "Member" and Employee "require the basic class" Counter ".
In the following we show the complete code of such a counter class:

class counter (object): number = 0 def __init __ (self): type (self). number + = 1 def __del __ (self): type (self). number - = 1 class account (counter): def __init __ (self, owner, account number, account balance, current account = 0): Counter .__ init __ (self)

Multiple inheritance

A class can also be a subclass of several base classes, i.e. it can inherit from several classes. The inheritance of several is known as Multiple inheritance
From a syntactic point of view, this is very simple: Instead of just one class within the brackets after the class name, you enter a comma-separated list of all base classes from which you want to inherit.

class NN (class 1, class 2, class 3, ...): The above class NN inherits from the classes "class 1", "class 2", "class 3" and so on.

1 in an interview with Graziano Lo Russo
Previous chapter: exception handling
Next chapter: slots