> Les bases > Modules et classes > Classes
Classes
Les bases
namespaces et scopes :
- namespaces : en fait, ce sont des python dictionaries. les noms qui ne sont pas dans des modules sont en fait dans l'espace de nom __main__ (et les noms built-ins sont dans __builtin__).
- 3 scopes sont recherchés successivement : local scope (local à la fonction), global scope (global au module) et built-in (noms built-in).
Définition de classe :
class MyClass:
"a simple class"
n = 0
def myMethod(self, a):
return 2 * a * self.myField
le code doit être executé avant que la classe ne soit définie (la définition peut très bien être à l'intérieur d'une fonction.
Constructeur : s'il existe, il doit s'appeler __init__ :
Définition d'une méthode :
- le premier argument doit toujours être l'objet lui-même et par convention, on l'appelle toujours self (mais c'est une convention seulement).
Instanciation d'une classe :
- var = MyClass() : constructeur sans arguments.
- var = MyClass('toto', 4) : constructeur avec arguments.
Appel d'un champ ou d'une méthode : si x est de la classe MyClass :
- tous les champs sont publics et toutes les méthodes sont publiques.
- on peut faire référence à x.myField même si myField n'a jamais été déclaré comme champ dans la classe !
- on peut détruire aussi un champ en faisant del x.myField.
- on ne peut faire référence a x.myMethod() que si myMethod est une fonction définie dans la classe (avec comme premier argument self).
- x.myMethod(arg) est équivalent à MyClass.myMethod(x, arg).
- on peut faire myMethod2 = x.myMethod; myMethod2() si on veut affecter la méthode (avec son objet) à une variable avant de l'appeler !
- attention : les attributs de champs peuvent surcharger les attributs de type methode !
- une méthode peut appeler une autre méthode avec self.myOtherMethod()
decorateur @property : permet de convertir une fonction en propriété, pour y accéder comme si c'était un champ :
class C:
def __init__(self, a):
self.a = a
@property
def A(self):
return self.a
on peut alors faire
obj = C(3); obj.A (au lieu de
obj.A()) pour accéder au champ a.
On ne peut pas avoir plusieurs méthodes de même nom et de signatures différentes.
Pour voir le constructeur d'une classe MyClass qui est définie dans le module (fichier) MyClass (sans avoir besoin de le qualifier complètement), faire :
from MyClass import MyClass
(mais il peut aussi être défini dans un fichier qui ne s'appelle pas du nom de la classe, par exemple myModule et alors il faut faire : from myModule import MyClass
Membres privés
Membres privés :
- tout champ ou méthode qui commence par 2 '_' et se termine au plus un '_' est privé.
- exemples : __myField, __myField_, __myMethod, __myMethod sont privés (alors que __myField__ ou __myMethod__ ne le sont pas).
- En fait, __myField et __myMethod sont remplacés par _MyClass__myField et _MyClass__myMethod et c'est pour ça qu'ils ne sont pas directement accessibles, c'est juste leur nom qui est changé ! On peut quand même y accéder avec le nouveau nom, mais évidemment, il ne faut pas le faire (sauf pour du test)
- les membres privés sont pareillement inaccessible dans une classe dérivée.
Pour avoir le nom de la classe à partir d'une instance : myObj.__class__.__name__
Héritage et reflexion
Héritage :
- class DerivedClass(BaseClass)
- les méthodes sont "virtuelles" : si objet de la classe de base est en fait de la classe dérivée et appelle une méthode definie dans la classe de base et surchargée dans la classe dérivée, c'est la méthode de la classe dérivée qui est appelée.
- dans la classe dérivée, on peut faire appel malgré tout à la classe de base avec BaseClass.myMethod(self, args)
python supporte l'héritage multiple :
- class DerivedClass(BaseClass1, BaseClass2)
- ordre de recherche des méthodes : depth-first, left-to-right (d'abord dans BaseClass1, puis dans ses classes de base, puis dans BaseClass2, puis dans ses classes de base).
Classe object :
- c'est la classe de base des nouvelles classes (qui incluent les dictionnaires, les listes, les types primitifs, ...)
- cette classe fournit en standard un certain nombre de champs et méthodes.
- si on hérite d'aucune autre classe, préférer hériter de object.
Fonctions pour tester l'appartenance aux classes :
- isinstance(obj, int) : renvoie True si l'objet est de la classe indiquée ou d'une classe dérivée.
- issubclass(bool, int) : renvoie True si la classe indiquée (ici bool) est dérivée de la 2ème classe indiquée.
Pour avoir le nom de la classe d'un objet :
- myObject.__class__ : donne la classe de l'objet (l'objet classe).
- myObject.__class__.__name__ : donne le nom de la classe de l'objet (une string).
Pour trouver la hiérarchie des classes pour un objet donné :
- myObject.__class__ : donne la classe de l'objet.
- myObject.__class__.__bases__ : donne les classes de base de la classe de l'objet.
- on peut continuer comme cela pour remonter à la classe de base initiale : myObject.__class__.__bases__[0].__base__[0] par exemple.
- myObject.__class__.__mro__ : donne la liste des classes auxquelles l'objet appartient (MRO : Method Resolution Order).
On peut accéder par noms aux champs et méthodes d'un objet :
- getattr(myObj, 'myField') : permet d'accéder au champ myField de l'objet myObj, donc équivalent à myObj.myField.
- getattr(myObj, 'myMethod') : permet d'accéder à la méthode myMethod de l'objet myObj, donc équivalent à myObj.myMethod.
- getattr(myObj, 'myField2', 'abc') : si le champ myField2 n'existe pas, renvoie 'abc' (marche aussi avec une méthode).
- si myMethod est une méthode de MyClass, alors, MyClass.myMethod est une méthode "unbound" : pas encore liée à un objet, et si on veut l'appeler, il faut donner l'objet en premier argument.
- si myMethod est une méthode de MyClass et myObj un objet de la classe MyClass, alors, myObj.myMethod est une méthode "bound" : elle est liée à l'objet, et si on veut l'appeler, il ne faut pas donner l'objet en premier argument, car il est déjà pris en compte !
On peut setter un champ avec : setattr(myObj, 'myField', 'myValue').
getter/setter générique d'une classe : permet d'exécuter du code quand on fixe un attribut de l'objet :
class MyClass:
def __init__(self):
pass
def __getattr__(self, name):
return self.__dict__[f'_{name}']
def __setattr__(self, name, value):
if name not in ['property1', 'property2']:
raise RuntimeError('Unknown property ' + name)
self.__dict__[f'_{name}'] = value
myObj = MyClass()
myObj.property1 = 45
print(myObj.property1)
Champs et méthodes statiques
Champs statique d'une classe :
Méthode statique d'une classe :
Méthode de classe pour construire un objet (quand constructeurs très divers nécessaires) :
@classmethod
def buildObject(cls, arg1, arg2):
self = MyClass.__new__(cls)
self.arg1 = arg1
self.arg2 = arg2
A appeler avec
myObj = MyClass.buildObject(arg1, arg2)
Divers
Pour accéder à tous les champs d'un objet : vars(myObj), ou aussi myObj.__dict__
Méthodes et champs rajoutés à une classe ou un objet :
- on peut définir des nouvelles méthodes pour une classe en dehors de celle-ci :
- quand on a un objet, et que l'on veut lui associer une propriété supplémentaire non déclarée dans l'objet, on peut faire : myObj.newProp = myValue (très pratique parfois pour une utilisation locale, même si bien sûr pas propre).
Méthode __new__ :
- c'est une méthode de classe qui est celle qui construit réellement l'objet.
- son premier argument doit être la classe, et elle doit renvoyer l'objet construit.
- __init__ est appelé juste après avec l'objet construit pour faire les initialisations.
- il est très rare d'avoir besoin de réimplémenter __new__, en général, on ne la définit pas.
Destructeur : __del__ : méthode qui est appelée quand l'instance va être détruite.
Classe singleton :
class Singleton(object):
_instance = None
def __new__(myClass):
if Singleton._instance is None:
Singleton._instance = object.__new__(myClass)
return Singleton._instance
def __init__(self):
self._var = 7
def getVar(self):
return self._var
s1 = Singleton()
print(s1.getVar())
print(id(s1))
s2 = Singleton()
print(s2.getVar())
print(id(s2))
c'est le même id d'objet qui est récupéré avec les 2 constructions.
Représentation d'un objet sous forme de chaîne de caractère :
- implémenter la méthode __str__ pour surcharger la représentation avec str.
- implémenter la méthode __repr__ pour surcharger la représentation avec repr.
- quand on fait un print d'un objet : si __str__ est défini, c'est cette méthode qui est utilisée, sinon, si __repr__ est définie, c'est elle qui est utilisée et sinon, c'est la représentation par défaut (du genre <__main__.MyClass instance at 0x7fbcaf00c9e0>
- différence entre str et repr : repr est censé fournir une représentation unique de l'objet, alors que str est censé fournir une représentation lisible de l'objet.
- il est recommandé de toujours implémenter __repr__ et de le faire éventuellement pour __str__
Appel au constructeur de la classe de base :
class A(object):
def __init__(self):
print("world")
class B(A):
def __init__(self):
print("hello")
super(B, self).__init__()
Définition des opérateurs : une classe peut réimplémenter les opérateurs en implémentant certaines fonctions :
Classe qui retourne un callable :
Copyright python-simple.com
programmer en python, tutoriel python, graphes en python, Aymeric Duclert