Dev avancé – Design Pattern : Le Singleton en Python

Dans cette article nous allons détailler l’utilisation du singleton en python.

En génie logiciel, le singleton est un patron de conception (design pattern) dont l’objectif est de restreindre l’instanciation d’une classe à un seul objet (ou bien à quelques objets seulement). Il est utilisé lorsqu’on a besoin exactement d’un objet pour coordonner des opérations dans un système. Le modèle est utile pour son efficacité, lorsque le système est plus rapide ou occupe moins de mémoire avec peu d’objets.

Pour en savoir plus sur les Design Patterns, consultez cet article.

Tu es débutant ? Ne perds pas ton temps ici, tu n’as pas encore le niveau !
Tu peux toujours essayer mais cet article risque de ressembler à de la philosophie quantique !
Je te conseille plutôt de télécharger mon guide (gratuit) pour apprendre à coder simplement et rapidement.

Principe

On implémente le singleton en écrivant une classe contenant une méthode qui crée une instance uniquement s’il n’en existe pas encore. Sinon elle renvoie une référence vers l’objet qui existe déjà. Dans beaucoup de langages de type objet, il faudra veiller à ce que le constructeur de la classe soit privé, afin de s’assurer que la classe ne puisse être instanciée autrement que par la méthode de création contrôlée.

Le singleton doit être implémenté avec précaution dans les applications multi-thread. Si deux threads exécutent en même temps la méthode de création alors que l’objet unique n’existe pas encore, il faut absolument s’assurer qu’un seul créera l’objet, et que l’autre obtiendra une référence vers ce nouvel objet.

La solution classique à ce problème consiste à utiliser l’exclusion mutuelle pour indiquer que l’objet est en cours d’instanciation.

Diagramme de classes

Singleton UML class diagram.svg

Le Singleton en Python

Implémentation simple

class  Singleton (object):
    instance = None       # Attribut statique de classe
    def __new__(cls): 
        "méthode de construction standard en Python"
        if cls.instance is None:
            cls.instance = object.__new__(cls)
        return cls.instance

# Utilisation
monSingleton1 =  Singleton()
monSingleton2 =  Singleton()

# monSingleton1 et monSingleton2 renvoient à la même instance
assert monSingleton1 is monSingleton2

Les deux variables référencent ici la même instance. Cette solution ne nécessite pas de méthode spécifique pour accéder à l’instance de classe (comparativement à d’autres langages, où il est d’usage d’implémenter une méthode getInstance(), par exemple).

Considérations avancées

D’après le développeur Python Alex Martelli, le patron de conception Singleton a un nom élégant mais trompeur, car il met l’accent sur l’identité plutôt que sur l’état de l’objet. D’où l’apparition d’un patron alternatif, appelé Borg 5, pour lequel toutes les instances partagent le même état. Il est généralement accepté par la communauté de développeurs Python que le partage d’état entre instances est plus élégant que la mise en cache de la création d’instances identiques lors de l’initialisation de la classe.

L’implémentation de cet état partagé est quasiment transparente en Python :

class Borg:
   __shared_state = {} # variable de classe contenant l'état à partager
   def __init__(self):
       # copie de l'état lors de l'initialisation d'une nouvelle instance
       self.__dict__ = self.__shared_state 
   # suivi de ce qui est nécessaire à la classe, et c'est tout!

L’implémentation ci-dessus peut être améliorée en tirant parti du nouveau type de classe de Python6, ne nécessitant ainsi qu’une seule instance :

class  Singleton (object):
   instance = None       
    def __new__(classe, *args, **kargs): 
        if classe.instance is None:
            classe.instance = object.__new__(classe, *args, **kargs)
        return classe.instance

# Utilisation:
monSingleton1 =  Singleton()
monSingleton2 =  Singleton()
 
# monSingleton1 et  monSingleton2 renvoient à la même instance.
assert monSingleton1 is monSingleton2
  • La méthode __init__ est exécutée pour chaque appel à Singleton() ;
  • Afin de permettre à une classe d’hériter d’un Singleton, la variable de classe instance devrait être un dictionnaire Python appartenant explicitement à la classe Singleton, comme illustré ci-dessous :
class  InheritableSingleton (object):
    # Dictionnaire Python référencant les instances déjà créés
    instances = {}
    def __new__(cls, *args, **kargs): 
        if InheritableSingleton.instances.get(cls) is None:
            InheritableSingleton.instances[cls] = object.__new__(cls, *args, **kargs)
        return InheritableSingleton.instances[cls]

Si tu as lu jusqu’ici mais que tu n’as rien compris, télécharges mon guide (gratuit) pour apprendre à coder simplement et rapidement !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *