POO
===
MRO
---
Quoi qu'il arrive, en python2 il faut hériter de :py:obj:`object`. On
bénéficie alors du :abbr:`MRO (Method Resolution Order)`, qui permet de se
débrouiller avec l'héritage multiple. Cf. le `tuto de Makina Corpus`_.
En python3 tous les objets héritent de :py:obj:`object` par défaut.
.. _tuto de Makina Corpus: http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
Setters/Getters
---------------
via des méthodes génériques
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Les setters et getters sont implicites en python, on peut cependant les créer
pour permettre une validation des entrées/sorties.
Les méthodes :py:meth:`object.__getattr__`, :py:meth:`object.__setattr__` et
:py:meth:`object.__delattr__` sont là pour intéragir avec des attributs
existants ou non.
Pour les objets héritants de :py:obj:`object`, on a également accès à la
méthode :py:meth:`object.__getattribute__`.
via @property
^^^^^^^^^^^^^
On peut également passer par les décorateurs :py:class:`property`.
Transforme une méthode en attribut (read-only)::
>>> class Parrot(object):
... def __init__(self):
... self._voltage = 100000
...
... @property
... def voltage(self):
... """Get the current voltage."""
... return self._voltage
>>> parrot = Parrot()
>>> parrot.voltage
100000
>>> parrot.voltage = 50
Traceback (most recent call last):
File "", line 1, in
AttributeError: can't set attribute
>>> parrot._voltage = 40
>>> parrot.voltage
40
Si on veut mettre en place des setter et deleter, la classe
``Parrot`` devient::
class Parrot(object):
def __init__(self):
self._voltage = 10000
@property
def voltage(self):
return self._voltage
@voltage.setter
def voltage(self, value):
self._voltage = value
@voltage.deleter
def voltage(self):
raise Exception("Impossible de supprimer cet élément")
Attributs privés
----------------
En python l'attribut privé est plus une convention qu'une règle. L'attribut
privé d'instance se préfixe d'un underscore (``_spam``)
On peut également créer des attributs privés de classe préfixés de deux
underscore et suffixé d'un underscore ou moins (``__spam`` ou ``__spam_``).
Ces attributs sont immédiatements renommés (``_ClassName__spam`` ou
``_ClassName__spam_``).
C'est ce qu'on appèle du :ref:`name mangling `
.. code-block:: python
>>> class A(object):
... __bonjour = "Hello"
...
>>> class B(A):
... __bonjour = "World"
...
>>> print(B._A__bonjour)
Hello
>>> print(B._B__bonjour)
World
>>> print(B.__yo) # L'attribut de base disparait
Traceback (most recent call last):
...
B.__bonjour
AttributeError: type object 'B' has no attribute '__bonjour'
Attributs spéciaux
------------------
+-------------------+---------------------------------------------------------------------------------------------------+
| Attribut | Description |
+===================+===================================================================================================+
| ``__call__`` | Rend l'objet appellable |
+-------------------+---------------------------------------------------------------------------------------------------+
| ``__dict__`` | Dictionnaire contenant tous les constantes, attributs et méthodes de l'objet/la classe |
+-------------------+---------------------------------------------------------------------------------------------------+
| ``__slots__`` | Pour la linéarisation d'objets, on sélectionne les attributs qui seront conservés en mémoire |
| | (à la manière de __all__ pour les modules) |
+-------------------+---------------------------------------------------------------------------------------------------+
| ``__[a-Z0-9]+_?`` | Les attributs préfixés de 2 « _ » et d'un « _ » au plus en suffixe sont des attributs spéciaux. |
| | Ils n'est pas possible de les overrider dans les classes filles. |
+-------------------+---------------------------------------------------------------------------------------------------+
D'autres attributs sont disponibles :ref:`ici `.
Métaclasses
-----------
Fabriquer/Modifier des classes à la volée, équivalent des :keyword:`lambda` mais pour
les classes.
Le constructeur d'une classe se fait en deux étapes.
#. Le __new__ s'occupe de créer la classe
#. le __init__ s'occupe de créer de l'instance.
En définissant le :py:meth:`__new__() ` on peut donc créer une
classe en lui ajoutant des attributs et méthodes.
.. note:: Pour créer une métaclasse, il faut la faire hériter de :py:obj:`type`.
.. code-block:: python
class MyClass(type):
def __new__(cls, name):
# ...
On peut également créer des métaclasse grâce à l'outils :py:mod:`abc`.
On va également pouvoir modifier le comportement de la classe (et non plus de l'instance).
On peut par exemple faire des surcharges de méthodes spéciales::
# Classe n'ayant pas de métaclasse particulière
class A:
pass
print(A) # affiche ""
# *** Exemple avec une métaclasse ***
# déclaration de la métaclasse
class MyMeta(type):
def __str__(self):
return "".format(self.__name__)
# déclaration de la classe utilisant la métaclasse
class A(metaclass=MyMeta):
pass
print(A) # affiche ""
On modifie donc bien le comportement de la classe et non plus de l'objet.
Des articles explicatifs `ici `_ et
`là `_
Cependant les métaclasses sont plutôt lourdes à mettre en place, et beaucoup d'utilisations ne nécessitent pas tout le paquetage.
Ils est donc parfois plus simple d'utiliser ``__init_subclass__()``, comme montré `ici `__ et `là `__.
Singleton
---------
Cet objet ne peut être instancié qu'une seule fois.
C'est dans la méthode :py:meth:`__new__() ` que cela doit être fait.
.. code-block:: python
class MySingleton():
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(MySingleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
.. warning:: Le désavantage de cette méthode est qu'en cas d'héritage multiple
une classe fille peut surcharger __new__.
Pour contrer cet effet il faut passer par une métaclasse.
.. code-block:: python
class MySingleton(type):
_instance = None
def __call__(cls, *args, **kwargs):
if cls._instances is None:
cls._instances = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances
#Python2
class MyClass(BaseClass):
__metaclass__ = MySingleton
#Python3
class MyClass(BaseClass, metaclass=MySingleton):
pass
Il existe un pattern de Singleton alternatif : le `Borg`_. Il permet le partage
des états entre objets et non de l'instance.
.. _Borg: http://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
Les patterns
------------
Brandon Rhodes fait un site détaillant les `Designs Patterns `__
appliqués à Python