[Design Pattern] Singleton et Multiton en Python

15 janvier 2008,

Pour le développement de Pyxoo je suis souvent confronté à la création de classes qui ne doivent proposer qu’une seule instance d’elles-même. La solution du Singleton semble bien évidemment la plus pertinente pour répondre à ce problème.

Malgré les différentes solutions trouvées sur la toile ici ou , un problème persiste en python : il est impossible de rendre privé un contructeur de classe comme en java.

Pour celà, un petit hack existe :

class MaClasse:
    __instance = None
    class __PRIVATE:pass

    @classmethod
    def getInstance(cls):
        if cls.__instance is None: cls.__instance = MaClasse(cls.__PRIVATE)
        return cls.__instance

    def __init__(self, privateAccess):
        if privateAccess is not self.__PRIVATE:
            raise Exception(“Contructeur privé”)

#Pour recuperer l’instance de la classe :
o = MaClasse.getInstance()

Nous venons de voir une manière simple de rendre privé le constructeur d’une classe et donc par la même occasion le design pattern Singleton qui, je le rappelle, fait partie de la famille “Création”.

Un avantage de cette méthode est la méthode statique getInstance() qui permet au premier coup d’oeil de comprendre qu’il s’agit d’un Singleton.

Un autre motif de conception peu connu mais néanmoins utile : le Multiton. Ce dernier est une sorte de singleton mais associé à une clé passée en paramètre de la méthode getInstance().

Un exemple concret de multiton est le ModelLocator implémenté dans la librairie Pyxoo. Un ModelLocator est associé à un Plugin passé en paramètre.

Voici un autre exemple d’implémentation du design pattern Multiton :

class MaClasse:
    __instances = dict()
    class __PRIVATE:pass

    @classmethod
    def getInstance(cls, unObjet):
        if unObject not in cls.__instances:
            cls.__instances[unObjet] = MaClasse(cls.__PRIVATE)
        return cls.__instances[unObjet]

    def __init__(self, privateAccess):
        if privateAccess is not self.__PRIVATE:
            raise Exception(“Contructeur privé”)

#Pour recuperer une instance de la classe

o1 = MaClasse.getInstance( “A1″ )
o2 = MaClasse.getInstance( “A1″ )
o3 = MaClasse.getInstance( “A2″ )

id(o1) == id(o2) #retourne vrai
id(o1) == id(o3) #retourne faux
 

Une méthode statique release() peut souvent être utile dans les deux cas présentés ci-dessus. En effet, il peut être nécessaire de réinitialiser l’instance.

class MaClasse:
    __instance = None
    class __PRIVATE:pass

    @classmethod
    def getInstance(cls):
        if cls.__instance is None:cls.__instance = MaClasse(cls.__PRIVATE)
        return cls.__instance

    def __init__(self, privateAccess):
        if privateAccess is not self.__PRIVATE:
            raise Exception( “Constructeur privé”)

    @classmethod
    def release(cls):
        cls.__instance = None

Comme vous pouvez le constater cette méthode réinitialise l’instance de la classe.

Voilà, c’est fini pour ce petit tutoriel, en espérant vous avoir été utile dans la compréhension de ces deux Design Pattern. :)

3 Commentaires

Brice Carpentier, le 15 janvier 2008 à 22:56

À mon humble avis, tu prends le problème à l’envers. En python (tout comme d’ailleurs en Javascript), le singleton n’a pas de réelle utilité car le module se substitue complètement à l’intérêt fonctionnel du singleton : s’assurer de la présence à l’instant t d’une seule instance d’une classe.

Voici un mail sur la PML qui traite de ce sujet :

http://mail.python.org/pipermail/python-list/2003-October/230201.html

Pythonniquement tien,
Brice

David, biologeek, le 12 avril 2008 à 9:37

Je suis d’accord avec Brice, voir à ce sujet la présentation suivante d’Alex Martelli : http://pyre.third-bit.com/blog/archives/886.html

SkiT, le 13 avril 2008 à 21:32

Oui en effet, je n’avais pas du tout pensé à utiliser directement le module pour répondre à ce problème. Par contre, je ne pense pas qu’il puisse répondre à la problématique du multiton…


Ajouter un commentaire