12. Classes and Objects
A class is the template from which objects are created. An object is said to be an instance of a class.

Note: The [shared] folder has been placed in the project's [Root Sources].
12.1. script [classes_01]: an Object class
The [classes_01] script demonstrates an obsolete use of classes:
Notes:
- lines 2-3: an empty [Object] class;
- Line 2: The class can be declared in three forms:
- class Object;
- class Object();
- class Object(object);
- line 3: another form of comment. This one, preceded by three "", can span multiple lines;
- line 7: instantiation of the Object class. The result is an address, as shown in lines 24–26;
- lines 8–9: direct initialization of two attributes of the object;
- line 17: copying references. The variables obj1 and obj2 are two pointers (references) to the same object;
- line 19: we modify the object pointed to by [obj2]. Since [obj1] and [obj2] point to the same object, the displays of the objects [obj1, obj2] in lines 21 and 22 will show that the object pointed to by [obj1] has changed;
- lines 24–26: these lines are intended to demonstrate that the variables [obj1] and [obj2] are equal. The output of line 26 will confirm this. In this comparison, it is the addresses of [obj1] and [obj2] that are equal;
- Every Python object is identified by a unique ID obtained using the expression [id(object)]. Lines 24 and 25 will show that the IDs of the objects pointed to by [obj1] and [obj2] are identical, thereby demonstrating that these two references point to the same object;
- Lines 27–29: The function [isinstance(expr, Type)] returns the Boolean value True if the expression [expr] is of type [Type]. Here, we will see that [obj1] is of type [Object], which seems natural, but also of type [object]. The [object] class is the parent class of all Python classes. By the property of class inheritance, a child class F has all the properties of its parent class P, and the function [isinstance(instance of F, P)] returns True;
- lines 30–32: show that type [int] is also a type [object]. All Python types derive from the [object] class;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
Process finished with exit code 0
12.2. Script [classes_02]: a Person class
The [classes_02] script demonstrates that a class's attributes are public: they can be accessed directly from outside the class. This is another example of a class usage that is not recommended. We include it, however, because you may occasionally encounter this type of code (Python allows it), and you need to be able to understand it.
Notes:
- lines 2–9: a class with a method;
- line 7: every method of a class must have the self object, which refers to the current object, as its first parameter. The [identity] method returns a string;
- line 15: instantiation of a [Person] object;
- lines 16–19: show that the object’s attributes can be created dynamically (they do not exist in the class definition);
- line 9: the class’s attributes are denoted by the notation [self.attribute];
- lines 23–24 demonstrate that the object [p] is both an instance of the [Person] class and of the [object] class;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
Process finished with exit code 0
12.3. Script [classes_03]: the Person class with a constructor
The script [classes_03] demonstrates the standard use of a class:
Notes:
- line 4: the class constructor is called __init__. As with other methods, its first parameter is self;
- line 20: a Person object is created using the class constructor;
- lines 13–15: the [identity] method returns a string representing the object’s content;
- line 22: displays the person's identity;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
Process finished with exit code 0
12.4. Script [classes_04]: static methods
We define the following [Utils] class (utils.py) in the [modules] folder:

Notes
- Line 3: The [@staticmethod] annotation indicates that the method marked with it is a class method, not an instance method. This is evident from the fact that the first parameter of the annotated method is not the [self] keyword. Therefore, the static method does not have access to the object's attributes. Instead of writing:
we write
Because we wrote [Utils.is_string_ok] above, the [is_string_ok] method is called a class method (the Utils class in this case). To achieve this, the [Utils.is_string_ok] method must be annotated with the [@staticmethod] keyword.
The static method [Utils.is_string_ok] allows us to verify here that a piece of data is a non-empty string.
The [classes_04] script uses the [Utils] class as follows:
- Lines 1-4: We use a configuration script;
The configuration script [config.py] is as follows:
- Line 9: The [shared] folder will be added to the Python Path;
The result of the execution is as follows:
12.5. Script [classes_05]: Attribute validity checks
The [classes_05] script introduces new concepts:
- definition of a custom exception type;
- definition of the [_str_] method, which is the default identity method for classes;
- definition of properties;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
Notes:
- lines 10–13: a MyException class derived from the BaseException class (we’ll cover this point a bit later). It adds no functionality to the latter. It’s only there to provide a custom exception;
- line 19: the constructor has default values for its parameters. Thus, the operation p = Person() is equivalent to p = Person("x", "y", 0);
- lines 34–45: the class properties. These are methods annotated with the [@property] keyword. They are used to set the values of the attributes;
- lines 47–77: the class’s setters. These are methods annotated with the [@attributsetter] keyword. They are used to set the value of the attributes;
- lines 48–54: the setter for the [first_name] attribute. This method will be called every time a value is assigned to the [first_name] attribute:
Line 2 will trigger the call [p.firstName(value)]. The advantage of using a setter to assign a value to an attribute is that, since the setter is a function, we can verify the validity of the value assigned to the attribute;
- line 51: we verify that the value assigned to the [first_name] attribute is a non-empty string. To do this, we use the static method [Utils.isStringOk] seen earlier;
- line 52: the value assigned to the [first_name] attribute is stripped of its leading and trailing whitespace and assigned to the [self.__first_name] attribute. Therefore, the [first_name] attribute itself is not used here. We could not have done otherwise, or we would have had an infinite recursive call. We could have used any attribute name. The fact that we used the attribute [__prénom] with two underscores at the beginning of the identifier has a special meaning: attributes preceded by two underscores are private to the class. This means they are not visible from outside the class. Therefore, we cannot write:
In fact, we’ll soon see that you can write it, but it doesn’t change the first name. It does something else;
- lines 53–54: if the value assigned to first_name is incorrect, an exception is thrown. This way, the calling code will know that its call is incorrect;
- lines 35–37: the [first_name] property. It will be called every time [p.first_name] is written in an expression. The [p.first_name()] method will then be called. Line 37: We return the value of the [__first_name] attribute, since we saw that the setter for the [first_name] attribute assigns its value to the private [__first_name] attribute;
- Lines 56–62: The setter for the [last_name] attribute is constructed similarly to that of the [first_name] attribute. The same applies to the setter for the [age] attribute on lines 64–77;
- although the properties [first_name, last_name, value] are not the actual attributes—which are in fact [__first_name, __last_name, __age]—we will continue to refer to them as the class attributes, since they are used as such;
- lines 19–28: The class constructor implicitly uses the setters for the attributes [first_name, last_name, age]. In fact, by writing [self.first_name = first_name] on line 26, the method [first_name(self, first_name)] is implicitly called. The validity of the [first_name] parameter will then be checked. The same applies to the other two attributes [last_name, age];
- with this model, we cannot assign incorrect values to the class attributes [first_name, last_name, age];
- lines 30–32: the __str__ function replaces the method previously called identity. The name [__str__] (two underscores before and after) is not insignificant. We will see this later;
- lines 83–86: instantiation of a person, followed by display of their identity;
- line 84: instantiation;
- Line 86: Display. The operation requires displaying the object p as a string. The Python interpreter automatically calls the method p.__str__() if it exists. This method serves the same purpose as the toString() method in Java or .NET languages;
- lines 87–89: Handling a potential MyException. Then displays the error;
- lines 91–99: same as above for a second person instantiated with incorrect parameters;
- lines 102–109: same as above for a third person instantiated with default parameters: no parameters are passed. The default values of these parameters in the constructor are then used here;
- lines 112–117: we said that the [__first_name] attribute was private and therefore normally inaccessible from outside the class. We want to verify this;
- lines 112–114: we assign a value to the [__first_name] attribute, then check the values of the [__first_name] and [first_name] attributes, which should normally be the same;
- lines 115–117: We repeat the operation, this time initializing the [first_name] attribute;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
Process finished with exit code 0
Notes
- lines 5-6: we see that the assignment [p.__first_name = "Gaëlle"] did not change the value of the [first_name] attribute, line 5;
- lines 7-8: we see that the assignment [p.first_name = "Sébastien"] has not changed the value of the [__first_name] attribute, line 8;
What can we conclude from this? That likely, the operation [p.__first_name = "Gaëlle"] created a public attribute [__first_name] for the class, but that this is different from the private attribute [__first_name] manipulated within it;
12.6. Script [classes_06]: Adding an object initialization method
The script [classes_06] adds a method to the [Person] class:
Notes:
- The difference from the previous script is in lines 30–33. We have added the initWithPerson method. This method calls the __init__ constructor. Unlike in typed languages, it is not possible to have methods with the same name that are distinguished by the nature of their parameters or their return values. Therefore, it is not possible to have multiple constructors that would create the object from different parameters, in this case an object of type Person;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
Process finished with exit code 0
12.7. Script [classes_07]: a list of Person objects
We will now place the [MyException] and [Person] classes in a module so we can use them without having to copy their code:

Both classes will be in the [myclasses.py] module above.
The [classes_07] script shows that we can have a list of objects:
Notes:
- line 7: we import the [Person] class;
- line 11: a list of objects of type [Person];
- lines 13–14: we iterate through this list to display each of its elements;
- line 14: the [print] function will display the string representing the object [group[i]]. By default, the [__str__] method of these objects will be called;
Results
C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.8. Script [classes_08]: creating a class derived from the Person class
We define the following [Teacher] class in the [myclasses] module:
# classe Enseignant
class Enseignant(Personne):
# constructeur
def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
# prénom : prénom de la personne
# nom : nom de la personne
# âge : âge de la personne
# discipline : discpline enseignée
# initialisation du parent
Personne.__init__(self, prénom, nom, âge)
# autres initialisations
self.discipline = discipline
# toString
def __str__(self) -> str:
return f"enseignant[{super().__str__()},{self.discipline}]"
# propriétés
@property
def discipline(self) -> str:
return self.__discipline
@discipline.setter
def discipline(self, discipline: str):
# la discipline doit être une chaîne non vide
if Utils.is_string_ok(discipline):
self.__discipline = discipline
else:
raise MyException("La discipline doit être une chaîne de caractères non vide")
- Line 2: declares the Teacher class as a subclass of the Person class. A subclass has all the properties (attributes and methods) of its parent class plus its own;
- line 13: the [Teacher] class defines a new attribute [subject];
- line 11: the constructor of the derived Teacher class must call the constructor of the parent Person class, passing it the parameters it expects;
- line 17: the [super()] function calls the parent class. Here, we call the [__str__] function of the parent class;
- lines 19–30: the getter and setter for the new [subject] attribute are defined;
The [classes_08] script uses the [Teacher] class as follows:
Notes:
- line 7: we import the [Person] and [Teacher] classes defined in the [myclasses.py] file;
- lines 11–14: we define a group of people and then display their identities;
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.9. Script [classes_09]: second class derived from the Person class
The script [classes_09] introduces the class [Student] derived from the class [Person]. This is defined as follows in the module [myclasses]:
# classe Etudiant
class Etudiant(Personne):
# constructeur
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
Personne.__init__(self, prénom, nom, âge)
self.formation = formation
# toString
def __str__(self: object) -> str:
return f"étudiant[{super().__str__()},{self.formation}]"
# propriétés
@property
def formation(self) -> str:
return self.__formation
@formation.setter
def formation(self, formation: str):
# la formation doit être une chaîne non vide
if Utils.is_string_ok(formation):
self.__formation = formation
else:
raise MyException("La formation doit être une chaîne de caractères non vide")
The [classes_09] script uses the [Student] class as follows:
Notes:
- This script is similar to the previous one.
Results
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
Process finished with exit code 0
12.10. Script [classes_10]: the [__dict__] property
The [classes_10] script introduces the [__dict__] property, which we will use frequently later on:
Comments
- lines 1-4: the application is set up;
- line 7: the [Student] class is imported;
- line 11: instantiation of a student;
- line 13: use of the predefined method [__dict__] (two underscores before and after the identifier);
The results are as follows:
- line 2, we obtain a dictionary whose keys are the object’s properties prefixed by the name of the class to which they belong. We will use this dictionary to create a bridge between the object and the dictionary;