Objets

En fait, tout ce qu’on manipule en Python est un objet. Et tous les objets sont toujours des instances d’une classe - on peut accéder à la classe qui a servi à instancier un objet avec la fonction type, par exemple:

class MaClasse:
    pass

mon_instance = MaClasse()
print(type(mon_instance))
# Affiche:
# <class '__main__.MaClasse'>

Mais aussi:

print(type(2))
# affiche: int

print(type("bonjour"))
# affiche: str

Donc en Python, les entiers sont des instances de la classe int, et les strings des instances de la classe str.

Ainsi, vous pouvez voir l’expression x = str(y) de deux façons:

  • Soit on appelle la fonction native str pour convertir y en string

  • Soit on crée une nouvelle instance de la classe str en appelant le constructeur de la classe str avec y en argument.

Notez que ça ne s’arrète pas là:

def ma_fonction():
    pass

print(type(ma_fonction))
# affiche: function

class MaClasse:
    def ma_méthode(self):
        pass

mon_instance = MaClasse()
print(type(mon_instance.ma_méthode))
# affiche: method

import sys
print(type(sys))
# affiche: module

Et même:

print(type(MaClasse))
# affiche: type

print(type(type))
# affiche: type

Et oui, les classes elles-mêmes sont des instances de classe! (la classe type)

Du coup en Python, le terme “objet” désigne toujours une instance de classe - même None est une instance d’une classe (elle s’appelle NoneType).

Ordre de résolution

Il est temps de revenir sur l’évaluation des expressions impliquant des attributs.

On a vu trois systèmes différents:

Appeler une fonction définie dans un module:

import mon_module
mon_module.ma_fonction()

Appeler une méthode d’instance définie dans une classe:

mon_instance = MaClasse()
mon_instance.ma_méthode()

Appeler une méthode de classe définie dans une classe:

MaClasse.ma_méthod_de_classe()

D’après ce qu’on a vu dans la section précédente, on sait maintenant que dans tous les cas, à gauche du point se situe un objet, et que tous les objets sont des instances d’une classe (appelé le « type » de l’objet).

Pour évaluer l’expression mon_objet.mon_attribut, où mon_objet` est une instance de mon_type, Python cherche toujours l’attribut dans deux endroits:

  • D’abord en tant qu’attribut de l’instance mon_objet

  • Ensuite, en tant qu’attribut de la classe mon_type

La recherche se poursuit ainsi en suivant toutes les classe parentes de mon_type.

On peut voir ce mécanisme en action dans le code suivant:

class A:
    def f1(self):
        print("f1 dans A")

    def f2(self):
        print("f2")


class B(A):
    @classmethod
    def f3(cls):
        print("f3")

    def f1(self):
        print("f1 dans B")


b = B()
b.f1()
b.f3()
b.f2()
# affiche:
# f1 dans B
# f3
# f2

Conclusion

Maintenant vous devriez comprendre pourquoi on dit parfois qu’en Python, tout est objet.

Dans un prochain chapitre, on expliquera pourquoi en plus de cela on peut dire qu’en Python, tout est dictionnaire.