Design Patterns Python ********************** Singleton ========= .. code-block:: python class Singleton(object): """ renvoie tjrs la même instance """ _ref = None def __new__(cls, *args, **kw): if cls._ref is None: cls._ref = super(Singleton, cls).__new__(cls, *args, **kw) return cls._ref class S(Singleton): pass Observer ======== .. code-block:: python class Event(object): """ Classe événement """ def __init__(self, name): self.name = name self._observateurs = [] def __iadd__(self, observateur): """ ajoute un observateur """ if not callable(observateur): raise TypeError("Doit être un objet appelable") observateurs = self._observateurs if observateur not in observateurs: observateurs.append(observateur) return self def __isub__(self, observateur): """ retire un observateur """ observateurs = self._observateurs if observateur in observateurs: observers.remove(observateur) return self def __call__(self, *args, **kw): """ déclenche l'événement auprès de tous les inscrits """ [observateur(*args, **kw) for observateur in self._observateurs] utilisation .. code-block:: python class Fenetre(object): def __init__(self, name): self.name = name self.titre = 'titre de la fenêtre' self.contenu = 'contenu de la fenêtre' self.titreEvent = Event('titre') self.contenuEvent = Event('contenu') globalTitreEvent = Event('titre global') def changeTitre(self, titre): self.titre = titre self.titreEvent(titre) Fenetre.globalTitreEvent(self.name, titre) def changeContenu(self, contenu): self.titre = contenu self.contenuEvent(contenu) class ObservateurTitre(object): def __init__(self, fenetre): fenetre.titreEvent += self def __call__(self, titre): print ('le titre a été changé en "%s" !' % titre) class ObservateurContenu(object): def __init__(self, fenetre): fenetre.contenuEvent += self.contenuChange def contenuChange(self, contenu): print('le contenu a été changé en "%s" !' % contenu) class ObservateurGlobal(object): def __init__(self, fenetre): fenetre.globalTitreEvent += self def __call__(self, name, titre): print('le titre de "%s" a été changé en "%s" !' % (name, titre)) fenetre = Fenetre('fenetre 1') fenetre2 = Fenetre('fenetre 2') ob1 = ObservateurTitre(fenetre) ob2 = ObservateurContenu(fenetre) ob3 = ObservateurGlobal(fenetre) fenetre.changeTitre('nouveau titre') fenetre.changeContenu('nouveau contenu') fenetre2.changeTitre('nouveau titre2') Gestion des additions et soustractions d'attribut ================================================= méthode simple mais tout les attributs sont testés .. code-block:: python class C(object): def __init__(self): self.x = 0 self.y = 0 def __setattr__(self, name, value): """Ne modifie pas x si la valeur est négative""" if name == 'x' and value < 0: return # Pour tout autre attribut on affecte la valeur object.__setattr__(self, name, value) méthode plus fine .. code-block:: python class UserList(object): def __init__(self, val=""): self._val = val def __add__(self, x): if len(self._val) > 0: return UserList(self._val + ',' + x) return UserList(x) def __iadd__(self, x): if len(self._val) > 0: self._val = self._val + ',' + x else: self._val = x return self def __sub__(self, x): return UserList(','.join([item for item in self._val.split(',') if item != x])) def __isub__(self, x): self._val = ','.join([item for item in self._val.split(',') if item != x]) return self def __str__(self): return str(self._val) def __repr__(self): return str(self._val) class Mail(object): def __init__(self, server="", subject="", to="", cc=None, dest=None): self._cc = UserList() self._for = UserList() self.server = server self.subject = subject self.msg = "" self.to = to if cc != None: self.cc += cc if dest != None: self.dest += dest def getcc(self): return self._cc def setcc(self, val): if not isinstance(val, UserList): val = UserList(val) self._cc = val def getdest(self): return self._for def setdest(self, val): if not isinstance(val, UserList): val = UserList(val) self._for = val cc = property(getcc, setcc) dest = property(getdest, setdest) mail = Mail(server='srvxxxx1', subject='test', to='informatiquepz@myprop-group.com', cc='f.bourel@myprop-group.com', dest='a.billard@myprop-group.com') mail.cc += 'f.gaborit@myprop-group.com' mail.cc = mail.cc + 'f.aoustin@myprop-group.com' mail.dest += 'appro3@myprop-group.com' print mail.cc print mail.dest mail.cc -= 'f.bourel@myprop-group.com' mail.cc = mail.cc - 'f.aoustin@myprop-group.com' print mail.cc producteur/consommateur ======================= Le principe est le suivant: * des objets produisent des données (ou d'autres objets) * des objets consomment les données (ou des objets) produits par les producteurs Il n'est possible que de consommer que ce qui est produit. ON peut limiter le nombre d'objet à consommer ou à produire à un moment t. Tout cela est le principe de la *queue* qui peut être du type FIFO, LILO ou prioritaire Il existe pour gérer cette queue le module *Queue* dans python. Dans l'exemple suivant nous gérons un magasin de stroumphs .. code-block:: python import random from Queue import * from threading import Thread import time enStock = Queue(10)#notre pile, qui peut contenir 10 elements max( 0 pour enlever la limite) class Magasin(Thread): """ Le 'producteur' : va remplir ses rayons de paquets de schtroumpfs""" def __init__(self): self.ouvert = True Thread.__init__(self) def run(self): while self.ouvert : while not enStock.full() : try : enStock.put("ajout d'un paquet de schtroumpf",True,1000) ### le Magasin va ajouter un paquet dans le rayon, et s'il y a plus de place, ### va attendre pendant 1 seconde qu'un paquet soit achete . except Full,e : print( "Il n'y a pas de place pour un paquet !\n"); else: print( "On ajoute un paquet de schtroumpfs dans le rayon\n") time.sleep(10) #on attend 10 secondes avant que le magasin aille alimenter le rayon class Client(Thread): """ le consommateur, va prendre un/des paquet(s) de schtroumpf""" def __init__(self,faim,numero): self.jAiFaim = faim self.numero = numero Thread.__init__(self) def run(self): while self.jAiFaim : time.sleep(1) #le client arrive au bout d'une seconde au rayon ok=False paquet = random.randint(1,4) #nombre de paquet qu'il va prendre i=1 print( "Le client n "+str(self.numero)+ " veut "+str(paquet)+ "paquet(s) de schtroumpfs\n") while i<=paquet : try: enStock.get(True,500) ok = True print( "n "+ str(self.numero) + ": Prend un paquet\n") except Empty, e: ok = False break else: i+=1 if ok : print( "Il reste "+ str( enStock.qsize() ) + " paquet(s) de schtroumpfs\n") else : print( "n "+ str(self.numero) + ": Il n'y a pas assez de paquets\n") self.jAiFaim = False if __name__=='__main__': magasin = Magasin() magasin.ouvert = True print("##### Le magasin ouvre ses portes #####") magasin.start() i=1 while i < 10: Client(faim = True ,numero = i).start() i=i+1 time.sleep(60) magasin.ouvert=False print("##### Le magasin ferme ses portes #####") les principales types de queues * Queue.Queue(maxsize=0) * Queue.LifoQueue(maxsize=0) * Queue.PriorityQueue(maxsize=0) les principales fonctions * Queue.qsize() * Queue.empty() * Queue.full() * Queue.put(item[, block[, timeout]]) * Queue.put_nowait(item) * Queue.get([block[, timeout]]) * Queue.get_nowait() * Queue.task_done() * Queue.join()