Python Design Patterns - Guida rapida

I design pattern vengono utilizzati per rappresentare il pattern utilizzato dagli sviluppatori per creare software o applicazioni web. Questi modelli vengono selezionati in base all'analisi dei requisiti. I modelli descrivono la soluzione al problema, quando e dove applicare la soluzione e le conseguenze dell'implementazione.

Struttura di un modello di progettazione

La documentazione del design pattern viene mantenuta in un modo che si concentra maggiormente sulla tecnologia utilizzata e in quali modi. Il diagramma seguente spiega la struttura di base della documentazione del modello di progettazione.

Nome modello

Descrive il modello in modo breve ed efficace.

Intento / Motivo

Descrive cosa fa il modello.

Applicabilità

Descrive l'elenco delle situazioni in cui il modello è applicabile.

Partecipanti e conseguenze

I partecipanti includono classi e oggetti che partecipano al modello di progettazione con un elenco di conseguenze che esistono con il modello.

Perché Python?

Python è un linguaggio di scripting open source. Dispone di librerie che supportano una varietà di modelli di progettazione. La sintassi di python è facile da capire e utilizza parole chiave inglesi.

Python fornisce supporto per l'elenco dei modelli di progettazione menzionati di seguito. Questi modelli di progettazione verranno utilizzati in questo tutorial:

  • Modello controller vista modello
  • Modello singleton
  • Modello di fabbrica
  • Modello di generatore
  • Modello prototipo
  • Modello di facciata
  • Modello di comando
  • Modello adattatore
  • Modello prototipo
  • Decoratore Pattern
  • Pattern proxy
  • Schema della catena di responsabilità
  • Pattern osservatore
  • Modello di stato
  • Modello di strategia
  • Modello modello
  • Modello Flyweight
  • Modello astratto di fabbrica
  • Pattern orientato agli oggetti

Vantaggi dell'utilizzo del modello di progettazione

Di seguito sono riportati i diversi vantaggi del design pattern:

  • I pattern forniscono agli sviluppatori una selezione di soluzioni collaudate per i problemi specificati.

  • Tutti i modelli di design sono indipendenti dal linguaggio.

  • I modelli aiutano a raggiungere la comunicazione e mantenere una buona documentazione.

  • Include un record di realizzazione per ridurre qualsiasi rischio tecnico per il progetto.

  • I modelli di progettazione sono altamente flessibili da usare e facili da capire.

Python è un linguaggio di scripting open source, di alto livello, interpretato, interattivo e orientato agli oggetti. È progettato per essere altamente leggibile. La sintassi del linguaggio Python è facile da capire e utilizza frequentemente parole chiave inglesi.

Caratteristiche del linguaggio Python

In questa sezione, impareremo le diverse caratteristiche del linguaggio Python.

Interpretato

Python viene elaborato in fase di esecuzione utilizzando l'interprete. Non è necessario compilare il programma prima dell'esecuzione. È simile a PERL e PHP.

Orientato agli oggetti

Python segue uno stile orientato agli oggetti e modelli di progettazione. Include la definizione della classe con varie caratteristiche come l'incapsulamento, il polimorfismo e molte altre.

Portatile

Codice Python scritto nel sistema operativo Windows e può essere utilizzato nel sistema operativo Mac. Il codice può essere riutilizzato e trasportabile secondo i requisiti.

Facile da codificare

La sintassi di Python è facile da capire e codificare. Qualsiasi sviluppatore può comprendere la sintassi di Python in poche ore. Python può essere descritto come "facile da programmare"

Estensibile

Se necessario, un utente può anche scrivere parte del codice Python in linguaggio C. È anche possibile inserire codice Python nel codice sorgente in diversi linguaggi come C ++. Questo rende Python un linguaggio estensibile.

Punti importanti

Considera i seguenti punti importanti relativi al linguaggio di programmazione Python:

  • Comprende metodi di programmazione funzionali e strutturati, nonché metodi di programmazione orientati agli oggetti.

  • Può essere utilizzato come linguaggio di scripting o come linguaggio di programmazione.

  • Include la raccolta automatica dei rifiuti.

  • Include tipi di dati dinamici di alto livello e supporta vari controlli dinamici del tipo.

  • Python include una funzionalità di integrazione con C, C ++ e linguaggi come Java.

Come scaricare il linguaggio Python nel tuo sistema?

Per scaricare il linguaggio Python nel tuo sistema, segui questo link -

https://www.python.org/downloads/

Include pacchetti per vari sistemi operativi come Windows, MacOS e distribuzioni Linux.

Gli strumenti importanti in Python

In questa sezione, impareremo brevemente alcuni strumenti importanti in Python.

Stringhe Python

La dichiarazione di base delle stringhe è la seguente:

str = 'Hello World!'

Elenchi di Python

Gli elenchi di python possono essere dichiarati come tipi di dati composti separati da virgole e racchiusi tra parentesi quadre ([]).

list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
tinylist = [123, 'john']

Tuple Python

Una tupla è un tipo di dati dinamico di Python, che consiste in un numero di valori separati da virgole. Le tuple sono racchiuse tra parentesi.

tinytuple = (123, 'john')

Dizionario Python

Il dizionario Python è un tipo di tabella hash. Una chiave del dizionario può essere quasi qualsiasi tipo di dati di Python. I tipi di dati sono generalmente numeri o stringhe.

tinydict = {'name': 'omkar','code':6734, 'dept': 'sales'}

Cosa costituisce un modello di progettazione in Python?

Python aiuta a costituire un modello di progettazione utilizzando i seguenti parametri:

  • Nome modello
  • Intent
  • Aliases
  • Motivation
  • Problem
  • Solution
  • Structure
  • Participants
  • Constraints
  • Codice d'esempio

Model View Controller è il design pattern più comunemente utilizzato. Gli sviluppatori trovano facile implementare questo modello di progettazione.

Di seguito è riportata un'architettura di base del Model View Controller:

Vediamo ora come funziona la struttura.

Modello

Consiste in pura logica applicativa, che interagisce con il database. Include tutte le informazioni per rappresentare i dati per l'utente finale.

Visualizza

La vista rappresenta i file HTML, che interagiscono con l'utente finale. Rappresenta i dati del modello all'utente.

Controller

Funge da intermediario tra vista e modello. Ascolta gli eventi attivati ​​dalla vista e interroga il modello per lo stesso.

Codice Python

Consideriamo un oggetto di base chiamato "Persona" e creiamo un modello di progettazione MVC.

Model.py

import json

class Person(object):
   def __init__(self, first_name = None, last_name = None):
      self.first_name = first_name
      self.last_name = last_name
   #returns Person name, ex: John Doe
   def name(self):
      return ("%s %s" % (self.first_name,self.last_name))
		
   @classmethod
   #returns all people inside db.txt as list of Person objects
   def getAll(self):
      database = open('db.txt', 'r')
      result = []
      json_list = json.loads(database.read())
      for item in json_list:
         item = json.loads(item)
         person = Person(item['first_name'], item['last_name'])
         result.append(person)
      return result

Richiede un metodo, che recupera tutti i record della tabella Person nel database. I record sono presentati in formato JSON.

Visualizza

Visualizza tutti i record recuperati all'interno del modello. La vista non interagisce mai con il modello; il controller fa questo lavoro (comunicando con il modello e la vista).

from model import Person
def showAllView(list):
   print 'In our db we have %i users. Here they are:' % len(list)
   for item in list:
      print item.name()
def startView():
   print 'MVC - the simplest example'
   print 'Do you want to see everyone in my db?[y/n]'
def endView():
   print 'Goodbye!'

Controller

Il controller interagisce con il modello tramite getAll() metodo che recupera tutti i record visualizzati all'utente finale.

from model import Person
import view

def showAll():
   #gets list of all Person objects
   people_in_db = Person.getAll()
   #calls view
   return view.showAllView(people_in_db)

def start():
   view.startView()
   input = raw_input()
   if input == 'y':
      return showAll()
   else:
      return view.endView()

if __name__ == "__main__":
   #running controller function
   start()

Questo modello limita la creazione di istanze di una classe a un oggetto. È un tipo di pattern creazionale e coinvolge solo una classe per creare metodi e oggetti specificati.

Fornisce un punto di accesso globale all'istanza creata.

Come implementare una classe singleton?

Il seguente programma dimostra l'implementazione della classe singleton in cui stampa le istanze create più volte.

class Singleton:
   __instance = None
   @staticmethod 
   def getInstance():
      """ Static access method. """
      if Singleton.__instance == None:
         Singleton()
      return Singleton.__instance
   def __init__(self):
      """ Virtually private constructor. """
      if Singleton.__instance != None:
         raise Exception("This class is a singleton!")
      else:
         Singleton.__instance = self
s = Singleton()
print s

s = Singleton.getInstance()
print s

s = Singleton.getInstance()
print s

Produzione

Il programma di cui sopra genera il seguente output:

Il numero di istanze create è lo stesso e non c'è differenza negli oggetti elencati nell'output.

Il modello di fabbrica rientra nella categoria dell'elenco dei modelli di creazione. Fornisce uno dei modi migliori per creare un oggetto. Nel modello di fabbrica, gli oggetti vengono creati senza esporre la logica al client e fare riferimento all'oggetto appena creato utilizzando un'interfaccia comune.

I modelli di fabbrica vengono implementati in Python utilizzando il metodo di fabbrica. Quando un utente chiama un metodo in modo tale da passare una stringa e il valore restituito come nuovo oggetto viene implementato tramite il metodo factory. Il tipo di oggetto utilizzato nel metodo factory è determinato dalla stringa che viene passata attraverso il metodo.

Nell'esempio seguente, ogni metodo include object come parametro, che viene implementato tramite il metodo factory.

Come implementare un modello di fabbrica?

Vediamo ora come implementare un pattern factory.

class Button(object):
   html = ""
   def get_html(self):
      return self.html

class Image(Button):
   html = "<img></img>"

class Input(Button):
   html = "<input></input>"

class Flash(Button):
   html = "<obj></obj>"

class ButtonFactory():
   def create_button(self, typ):
      targetclass = typ.capitalize()
      return globals()[targetclass]()

button_obj = ButtonFactory()
button = ['image', 'input', 'flash']
for b in button:
   print button_obj.create_button(b).get_html()

La classe button aiuta a creare i tag html e la pagina html associata. Il client non avrà accesso alla logica del codice e l'output rappresenta la creazione della pagina html.

Produzione

Spiegazione

Il codice python include la logica dei tag html, il cui valore specificato. L'utente finale può dare un'occhiata al file HTML creato dal codice Python.

Builder Pattern è un modello di progettazione unico che aiuta a costruire oggetti complessi utilizzando oggetti semplici e utilizza un approccio algoritmico. Questo modello di design rientra nella categoria del modello creazionale. In questo modello di progettazione, una classe builder costruisce l'oggetto finale in una procedura dettagliata. Questo builder è indipendente da altri oggetti.

Vantaggi di Builder Pattern

  • Fornisce una chiara separazione e uno strato univoco tra la costruzione e la rappresentazione di un oggetto specificato creato dalla classe.

  • Fornisce un migliore controllo sul processo di costruzione del modello creato.

  • Fornisce lo scenario perfetto per cambiare la rappresentazione interna degli oggetti.

Come implementare il pattern builder?

In questa sezione impareremo come implementare il pattern builder.

class Director:
   __builder = None
   
   def setBuilder(self, builder):
      self.__builder = builder
   
   def getCar(self):
      car = Car()
      
      # First goes the body
      body = self.__builder.getBody()
      car.setBody(body)
      
      # Then engine
      engine = self.__builder.getEngine()
      car.setEngine(engine)
      
      # And four wheels
      i = 0
      while i < 4:
         wheel = self.__builder.getWheel()
			car.attachWheel(wheel)
         i += 1
      return car

# The whole product
class Car:
   def __init__(self):
      self.__wheels = list()
      self.__engine = None
      self.__body = None

   def setBody(self, body):
      self.__body = body

   def attachWheel(self, wheel):
      self.__wheels.append(wheel)

   def setEngine(self, engine):
      self.__engine = engine

   def specification(self):
      print "body: %s" % self.__body.shape
      print "engine horsepower: %d" % self.__engine.horsepower
      print "tire size: %d\'" % self.__wheels[0].size

class Builder:
      def getWheel(self): pass
      def getEngine(self): pass
      def getBody(self): pass

class JeepBuilder(Builder):
   
   def getWheel(self):
      wheel = Wheel()
      wheel.size = 22
      return wheel
   
   def getEngine(self):
      engine = Engine()
      engine.horsepower = 400
      return engine
   
   def getBody(self):
      body = Body()
      body.shape = "SUV"
      return body

# Car parts
class Wheel:
   size = None

class Engine:
   horsepower = None

class Body:
   shape = None

def main():
   jeepBuilder = JeepBuilder() # initializing the class
   
   director = Director()
   
   # Build Jeep
   print "Jeep"
   director.setBuilder(jeepBuilder)
   jeep = director.getCar()
   jeep.specification()
   print ""

if __name__ == "__main__":
   main()

Produzione

Il programma di cui sopra genera il seguente output:

Il modello di progettazione del prototipo aiuta a nascondere la complessità delle istanze create dalla classe. Il concetto dell'oggetto esistente sarà diverso da quello del nuovo oggetto, che viene creato da zero.

L'oggetto appena copiato potrebbe avere alcune modifiche nelle proprietà, se necessario. Questo approccio consente di risparmiare tempo e risorse necessarie per lo sviluppo di un prodotto.

Come implementare un modello prototipo?

Vediamo ora come implementare un modello prototipo.

import copy

class Prototype:

   _type = None
   _value = None

   def clone(self):
      pass

   def getType(self):
      return self._type

   def getValue(self):
      return self._value

class Type1(Prototype):

   def __init__(self, number):
      self._type = "Type1"
      self._value = number

   def clone(self):
      return copy.copy(self)

class Type2(Prototype):

   """ Concrete prototype. """

   def __init__(self, number):
      self._type = "Type2"
      self._value = number

   def clone(self):
      return copy.copy(self)

class ObjectFactory:

   """ Manages prototypes.
   Static factory, that encapsulates prototype
   initialization and then allows instatiation
   of the classes from these prototypes.
   """

   __type1Value1 = None
   __type1Value2 = None
   __type2Value1 = None
   __type2Value2 = None

   @staticmethod
   def initialize():
      ObjectFactory.__type1Value1 = Type1(1)
      ObjectFactory.__type1Value2 = Type1(2)
      ObjectFactory.__type2Value1 = Type2(1)
      ObjectFactory.__type2Value2 = Type2(2)

   @staticmethod
   def getType1Value1():
      return ObjectFactory.__type1Value1.clone()

   @staticmethod
   def getType1Value2():
      return ObjectFactory.__type1Value2.clone()

   @staticmethod
   def getType2Value1():
      return ObjectFactory.__type2Value1.clone()

   @staticmethod
   def getType2Value2():
      return ObjectFactory.__type2Value2.clone()

def main():
   ObjectFactory.initialize()
   
   instance = ObjectFactory.getType1Value1()
   print "%s: %s" % (instance.getType(), instance.getValue())
   
   instance = ObjectFactory.getType1Value2()
   print "%s: %s" % (instance.getType(), instance.getValue())
   
   instance = ObjectFactory.getType2Value1()
   print "%s: %s" % (instance.getType(), instance.getValue())
   
   instance = ObjectFactory.getType2Value2()
   print "%s: %s" % (instance.getType(), instance.getValue())

if __name__ == "__main__":
   main()

Produzione

Il programma precedente genererà il seguente output:

L'output aiuta a creare nuovi oggetti con quelli esistenti ed è chiaramente visibile nell'output di cui sopra.

Il modello di progettazione della facciata fornisce un'interfaccia unificata a una serie di interfacce in un sottosistema. Definisce un'interfaccia di livello superiore utilizzabile da qualsiasi sottosistema.

Una classe di facciata sa quale sottosistema è responsabile di una richiesta.

Come progettare un modello di facciata?

Vediamo ora come progettare un modello di facciata.

class _IgnitionSystem(object):
   
   @staticmethod
   def produce_spark():
      return True

class _Engine(object):

   def __init__(self):
      self.revs_per_minute = 0

   def turnon(self):
      self.revs_per_minute = 2000
   
   def turnoff(self):
      self.revs_per_minute = 0

class _FuelTank(object):
   
   def __init__(self, level=30):
      self._level = level
   
   @property
   def level(self):
      return self._level
   
   @level.setter
	def level(self, level):
      self._level = level

class _DashBoardLight(object):

   def __init__(self, is_on=False):
      self._is_on = is_on

   def __str__(self):
      return self.__class__.__name__

   @property
   def is_on(self):
      return self._is_on
   
   @is_on.setter
   def is_on(self, status):
      self._is_on = status
   
   def status_check(self):
      if self._is_on:
         print("{}: ON".format(str(self)))
      else:
         print("{}: OFF".format(str(self)))

class _HandBrakeLight(_DashBoardLight):
   pass

class _FogLampLight(_DashBoardLight):
   pass

class _Dashboard(object):
   
   def __init__(self):
      self.lights = {"handbreak": _HandBrakeLight(), "fog": _FogLampLight()}
   
   def show(self):
	   for light in self.lights.values():
      light.status_check()

# Facade
class Car(object):
   
   def __init__(self):
      self.ignition_system = _IgnitionSystem()
      self.engine = _Engine()
      self.fuel_tank = _FuelTank()
      self.dashboard = _Dashboard()
   
   @property
   def km_per_litre(self):
      return 17.0
   
   def consume_fuel(self, km):
      litres = min(self.fuel_tank.level, km / self.km_per_litre)
      self.fuel_tank.level -= litres
   
   def start(self):
      print("\nStarting...")
      self.dashboard.show()
      if self.ignition_system.produce_spark():
         self.engine.turnon()
      else:
         print("Can't start. Faulty ignition system")
   
   def has_enough_fuel(self, km, km_per_litre):
      litres_needed = km / km_per_litre
      if self.fuel_tank.level > litres_needed:
         return True
      else:
         return False
	   
      def drive(self, km = 100):
         print("\n")
         if self.engine.revs_per_minute > 0:
            while self.has_enough_fuel(km, self.km_per_litre):
               self.consume_fuel(km)
               print("Drove {}km".format(km))
               print("{:.2f}l of fuel still left".format(self.fuel_tank.level))
         else:
            print("Can't drive. The Engine is turned off!")
         
         def park(self):
            print("\nParking...")
            self.dashboard.lights["handbreak"].is_on = True
            self.dashboard.show()
            self.engine.turnoff()
         
         def switch_fog_lights(self, status):
            print("\nSwitching {} fog lights...".format(status))
            boolean = True if status == "ON" else False
            self.dashboard.lights["fog"].is_on = boolean
            self.dashboard.show()
         
         def fill_up_tank(self):
            print("\nFuel tank filled up!")
            self.fuel_tank.level = 100
				
# the main function is the Client
def main():
   car = Car()
   car.start()
   car.drive()
   car.switch_fog_lights("ON")
   car.switch_fog_lights("OFF")
	car.park()
   car.fill_up_tank()
   car.drive()
   car.start()
   car.drive()

if __name__ == "__main__":
   main()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Questo programma è progettato con uno scenario. È quello di avviare il motore di un'auto o di qualsiasi veicolo in movimento. Se osservi il codice, include le funzioni associate per guidare, parcheggiare e anche consumare carburante.

Command Pattern aggiunge un livello di astrazione tra le azioni e include un oggetto, che richiama queste azioni.

In questo modello di progettazione, il client crea un oggetto comando che include un elenco di comandi da eseguire. L'oggetto comando creato implementa un'interfaccia specifica.

Di seguito è riportata l'architettura di base del modello di comando:

Come implementare il modello di comando?

Vedremo ora come implementare il design pattern.

def demo(a,b,c):
   print 'a:',a
   print 'b:',b
   print 'c:',c

class Command:
   def __init__(self, cmd, *args):
      self._cmd=cmd
      self._args=args

   def __call__(self, *args):
      return apply(self._cmd, self._args+args)
cmd = Command(dir,__builtins__)
print cmd()

cmd = Command(demo,1,2)
cmd(3)

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

L'output implementa tutti i comandi e le parole chiave elencati nel linguaggio Python. Stampa i valori necessari delle variabili.

Il modello dell'adattatore funziona come un ponte tra due interfacce incompatibili. Questo tipo di modello di progettazione rientra nel modello strutturale poiché questo modello combina la capacità di due interfacce indipendenti.

Questo modello coinvolge una singola classe, che è responsabile di unire funzionalità di interfacce indipendenti o incompatibili. Un esempio di vita reale potrebbe essere il caso di un lettore di schede, che funge da adattatore tra la scheda di memoria e un laptop. Si collega la scheda di memoria al lettore di schede e il lettore di schede al laptop in modo che la scheda di memoria possa essere letta tramite il laptop.

Il modello di progettazione dell'adattatore aiuta a lavorare insieme le classi. Converte l'interfaccia di una classe in un'altra interfaccia in base ai requisiti. Il modello include una speciazione un polimorfismo che nomina un nome e più forme. Dire per una classe di forma che può essere utilizzata secondo i requisiti raccolti.

Esistono due tipi di modello di adattatore:

Modello adattatore oggetto

Questo modello di progettazione si basa sull'implementazione degli oggetti. Quindi, è chiamato Object Adapter Pattern.

Modello adattatore di classe

Questo è un modo alternativo per implementare il modello di progettazione dell'adattatore. Il modello può essere implementato utilizzando più eredità.

Come implementare il pattern dell'adattatore?

Vediamo ora come implementare il pattern dell'adattatore.

class EuropeanSocketInterface:
   def voltage(self): pass

   def live(self): pass
   def neutral(self): pass
   def earth(self): pass

# Adaptee
class Socket(EuropeanSocketInterface):
   def voltage(self):
      return 230

	def live(self):
      return 1
   
   def neutral(self):
      return -1
   
   def earth(self):
      return 0

# Target interface
class USASocketInterface:
   def voltage(self): pass
   def live(self): pass
   def neutral(self): pass

# The Adapter
class Adapter(USASocketInterface):
   __socket = None
   def __init__(self, socket):
      self.__socket = socket
   
   def voltage(self):
      return 110
   
   def live(self):
      return self.__socket.live()
   
   def neutral(self):
      return self.__socket.neutral()

# Client
class ElectricKettle:
   __power = None
   
   def __init__(self, power):
	   self.__power = power
   
   def boil(self):
      if self.__power.voltage() > 110:
         print "Kettle on fire!"
      else:
         if self.__power.live() == 1 and \
            self.__power.neutral() == -1:
            print "Coffee time!"
         else:
            print "No power."

def main():
   # Plug in
   socket = Socket()
   adapter = Adapter(socket)
   kettle = ElectricKettle(adapter)
	
   # Make coffee
   kettle.boil()
	
   return 0
	
if __name__ == "__main__":
   main()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Il codice include l'interfaccia dell'adattatore con vari parametri e attributi. Include Adaptee insieme all'interfaccia Target che implementa tutti gli attributi e visualizza l'output come visibile.

Il motivo Decorator consente a un utente di aggiungere nuove funzionalità a un oggetto esistente senza alterarne la struttura. Questo tipo di modello di progettazione rientra nel modello strutturale poiché questo modello funge da involucro per la classe esistente.

Questo modello crea una classe decoratore, che avvolge la classe originale e fornisce funzionalità aggiuntive mantenendo intatta la firma dei metodi di classe.

Il motivo di un motivo decorativo è attribuire dinamicamente responsabilità aggiuntive a un oggetto.

Come implementare il modello di progettazione del decoratore

Il codice indicato di seguito è una semplice dimostrazione di come implementare il modello di progettazione del decoratore in Python. L'illustrazione prevede la dimostrazione di una caffetteria nel formato di una lezione. La classe di caffè creata è un abstract, il che significa che non può essere istanziata.

import six
from abc import ABCMeta

@six.add_metaclass(ABCMeta)
class Abstract_Coffee(object):

   def get_cost(self):
      pass

   def get_ingredients(self):
      pass
   
   def get_tax(self):
      return 0.1*self.get_cost()

class Concrete_Coffee(Abstract_Coffee):
   
   def get_cost(self):
      return 1.00
   
   def get_ingredients(self):
      return 'coffee'

@six.add_metaclass(ABCMeta)
class Abstract_Coffee_Decorator(Abstract_Coffee):
   
   def __init__(self,decorated_coffee):
      self.decorated_coffee = decorated_coffee
   
   def get_cost(self):
      return self.decorated_coffee.get_cost()
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients()

class Sugar(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost()
   
   def get_ingredients(self):
	   return self.decorated_coffee.get_ingredients() + ', sugar'

class Milk(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost() + 0.25
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients() + ', milk'

class Vanilla(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost() + 0.75
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients() + ', vanilla'

L'implementazione della classe astratta della caffetteria viene eseguita con un file separato come indicato di seguito -

import coffeeshop

myCoffee = coffeeshop.Concrete_Coffee()
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Milk(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Vanilla(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Sugar(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

Produzione

Il programma di cui sopra genera il seguente output:

Il modello di progettazione proxy include un nuovo oggetto, chiamato "Proxy" al posto di un oggetto esistente, chiamato "Soggetto reale". L'oggetto proxy creato dal soggetto reale deve trovarsi sulla stessa interfaccia in modo tale che il client non debba avere alcuna idea che il proxy venga utilizzato al posto dell'oggetto reale. Le richieste generate dal client al proxy vengono passate attraverso il soggetto reale.

La rappresentazione UML del modello proxy è la seguente:

Come implementare il modello proxy?

Vediamo ora come implementare il modello proxy.

class Image:
   def __init__( self, filename ):
      self._filename = filename
   
   def load_image_from_disk( self ):
      print("loading " + self._filename )
   
   def display_image( self ):
      print("display " + self._filename)

class Proxy:
   def __init__( self, subject ):
      self._subject = subject
      self._proxystate = None

class ProxyImage( Proxy ):
   def display_image( self ):
      if self._proxystate == None:
         self._subject.load_image_from_disk()
         self._proxystate = 1
      print("display " + self._subject._filename )

proxy_image1 = ProxyImage ( Image("HiRes_10Mb_Photo1") )
proxy_image2 = ProxyImage ( Image("HiRes_10Mb_Photo2") )

proxy_image1.display_image() # loading necessary
proxy_image1.display_image() # loading unnecessary
proxy_image2.display_image() # loading necessary
proxy_image2.display_image() # loading unnecessary
proxy_image1.display_image() # loading unnecessary

Produzione

Il programma di cui sopra genera il seguente output:

Il design del modello proxy aiuta a replicare le immagini che abbiamo creato. La funzione display_image () aiuta a controllare se i valori vengono stampati nel prompt dei comandi.

Il modello della catena di responsabilità viene utilizzato per ottenere un accoppiamento libero nel software in cui una specifica richiesta del client viene passata attraverso una catena di oggetti inclusi in esso. Aiuta a costruire una catena di oggetti. La richiesta entra da un'estremità e si sposta da un oggetto all'altro.

Questo modello consente a un oggetto di inviare un comando senza sapere quale oggetto gestirà la richiesta.

Come implementare il modello della catena di responsabilità?

Vedremo ora come implementare il modello della catena di responsabilità.

class ReportFormat(object):
   PDF = 0
   TEXT = 1
class Report(object):
   def __init__(self, format_):
      self.title = 'Monthly report'
      self.text = ['Things are going', 'really, really well.']
      self.format_ = format_

class Handler(object):
   def __init__(self):
      self.nextHandler = None

   def handle(self, request):
      self.nextHandler.handle(request)

class PDFHandler(Handler):

   def handle(self, request):
      if request.format_ == ReportFormat.PDF:
         self.output_report(request.title, request.text)
      else:
         super(PDFHandler, self).handle(request)
	
   def output_report(self, title, text):
      print '<html>'
      print ' <head>'
      print ' <title>%s</title>' % title
      print ' </head>'
      print ' <body>'
      for line in text:
         print ' <p>%s

' % line print ' </body>' print '</html>' class TextHandler(Handler): def handle(self, request): if request.format_ == ReportFormat.TEXT: self.output_report(request.title, request.text) else: super(TextHandler, self).handle(request) def output_report(self, title, text): print 5*'*' + title + 5*'*' for line in text: print line class ErrorHandler(Handler): def handle(self, request): print "Invalid request" if __name__ == '__main__': report = Report(ReportFormat.TEXT) pdf_handler = PDFHandler() text_handler = TextHandler() pdf_handler.nextHandler = text_handler text_handler.nextHandler = ErrorHandler() pdf_handler.handle(report)

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Il codice precedente crea un report per le attività mensili in cui invia i comandi attraverso ciascuna funzione. Sono necessari due gestori: per PDF e per testo. Stampa l'output una volta che l'oggetto richiesto esegue ciascuna funzione.

In questo modello, gli oggetti sono rappresentati come osservatori che attendono l'attivazione di un evento. Un osservatore si attacca al soggetto una volta che si verifica l'evento specificato. Quando l'evento si verifica, il soggetto dice agli osservatori che si è verificato.

Il seguente diagramma UML rappresenta il modello dell'osservatore:

Come implementare il pattern dell'osservatore?

Vediamo ora come implementare il pattern dell'osservatore.

import threading
import time
import pdb

class Downloader(threading.Thread):
   
   def run(self):
      print 'downloading'
      for i in range(1,5):
         self.i = i
         time.sleep(2)
			print 'unfunf'
         return 'hello world'

class Worker(threading.Thread):
   def run(self):
      for i in range(1,5):
         print 'worker running: %i (%i)' % (i, t.i)
         time.sleep(1)
         t.join()

         print 'done'

t = Downloader()
t.start()

time.sleep(1)

t1 = Worker()
t1.start()

t2 = Worker()
t2.start()

t3 = Worker()
t3.start()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Il codice sopra spiega la procedura per scaricare un particolare risultato. Secondo la logica del pattern dell'osservatore, ogni oggetto viene trattato come osservatore. Stampa l'output quando viene attivato l'evento.

Fornisce un modulo per macchine a stati, implementate utilizzando sottoclassi, derivate da una classe di macchina a stati specificata. I metodi sono indipendenti dallo stato e causano transizioni dichiarate utilizzando decoratori.

Come implementare il modello statale?

L'implementazione di base del modello di stato è mostrata di seguito:

class ComputerState(object):

   name = "state"
   allowed = []

   def switch(self, state):
      """ Switch to new state """
      if state.name in self.allowed:
         print 'Current:',self,' => switched to new state',state.name
         self.__class__ = state
      else:
         print 'Current:',self,' => switching to',state.name,'not possible.'

   def __str__(self):
      return self.name

class Off(ComputerState):
   name = "off"
   allowed = ['on']

class On(ComputerState):
   """ State of being powered on and working """
   name = "on"
   allowed = ['off','suspend','hibernate']

class Suspend(ComputerState):
   """ State of being in suspended mode after switched on """
   name = "suspend"
   allowed = ['on']

class Hibernate(ComputerState):
   """ State of being in hibernation after powered on """
   name = "hibernate"
   allowed = ['on']

class Computer(object):
   """ A class representing a computer """
   
   def __init__(self, model='HP'):
      self.model = model
      # State of the computer - default is off.
      self.state = Off()
   
   def change(self, state):
      """ Change state """
      self.state.switch(state)

if __name__ == "__main__":
   comp = Computer()
   comp.change(On)
   comp.change(Off)
   comp.change(On)
   comp.change(Suspend)
   comp.change(Hibernate)
   comp.change(On)
   comp.change(Off)

Produzione

Il programma di cui sopra genera il seguente output:

Il modello strategico è un tipo di modello comportamentale. L'obiettivo principale del pattern strategico è consentire al cliente di scegliere tra diversi algoritmi o procedure per completare l'attività specificata. Diversi algoritmi possono essere scambiati dentro e fuori senza complicazioni per l'attività menzionata.

Questo modello può essere utilizzato per migliorare la flessibilità quando si accede a risorse esterne.

Come implementare il modello strategico?

Il programma mostrato di seguito aiuta nell'implementazione del modello di strategia.

import types

class StrategyExample:
   def __init__(self, func = None):
      self.name = 'Strategy Example 0'
      if func is not None:
         self.execute = types.MethodType(func, self)

   def execute(self):
      print(self.name)

def execute_replacement1(self): 
   print(self.name + 'from execute 1')

def execute_replacement2(self):
   print(self.name + 'from execute 2')

if __name__ == '__main__':
   strat0 = StrategyExample()
   strat1 = StrategyExample(execute_replacement1)
   strat1.name = 'Strategy Example 1'
   strat2 = StrategyExample(execute_replacement2)
   strat2.name = 'Strategy Example 2'
   strat0.execute()
   strat1.execute()
   strat2.execute()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Fornisce un elenco di strategie dalle funzioni, che eseguono l'output. L'obiettivo principale di questo modello di comportamento è il comportamento.

Un modello di modello definisce un algoritmo di base in una classe base utilizzando un'operazione astratta in cui le sottoclassi sovrascrivono il comportamento concreto. Il modello modello mantiene la struttura dell'algoritmo in un metodo separato. Questo metodo è denominato metodo modello.

Di seguito sono riportate le diverse caratteristiche del modello modello:

  • Definisce lo scheletro dell'algoritmo in un'operazione

  • Include sottoclassi, che ridefiniscono alcuni passaggi di un algoritmo.

class MakeMeal:

   def prepare(self): pass
   def cook(self): pass
   def eat(self): pass

   def go(self):
      self.prepare()
      self.cook()
      self.eat()

class MakePizza(MakeMeal):
   def prepare(self):
      print "Prepare Pizza"
   
   def cook(self):
      print "Cook Pizza"
   
   def eat(self):
      print "Eat Pizza"

class MakeTea(MakeMeal):
   def prepare(self):
      print "Prepare Tea"
	
   def cook(self):
      print "Cook Tea"
   
   def eat(self):
      print "Eat Tea"

makePizza = MakePizza()
makePizza.go()

print 25*"+"

makeTea = MakeTea()
makeTea.go()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Questo codice crea un modello per preparare il pasto. Qui, ogni parametro rappresenta l'attributo per creare una parte del pasto come tè, pizza, ecc.

L'output rappresenta la visualizzazione degli attributi.

Il modello flyweight rientra nella categoria dei modelli di progettazione strutturale. Fornisce un modo per diminuire il numero di oggetti. Include varie funzionalità che aiutano a migliorare la struttura dell'applicazione. La caratteristica più importante degli oggetti flyweight è immutabile. Ciò significa che non possono essere modificati una volta costruiti. Il modello utilizza una HashMap per memorizzare gli oggetti di riferimento.

Come implementare il modello flyweight?

Il seguente programma aiuta a implementare il modello dei pesi mosca:

class ComplexGenetics(object):
   def __init__(self):
      pass
   
   def genes(self, gene_code):
      return "ComplexPatter[%s]TooHugeinSize" % (gene_code)
class Families(object):
   family = {}
   
   def __new__(cls, name, family_id):
      try:
         id = cls.family[family_id]
      except KeyError:
         id = object.__new__(cls)
         cls.family[family_id] = id
      return id
   
   def set_genetic_info(self, genetic_info):
      cg = ComplexGenetics()
      self.genetic_info = cg.genes(genetic_info)
   
   def get_genetic_info(self):
      return (self.genetic_info)

def test():
   data = (('a', 1, 'ATAG'), ('a', 2, 'AAGT'), ('b', 1, 'ATAG'))
   family_objects = []
   for i in data:
      obj = Families(i[0], i[1])
      obj.set_genetic_info(i[2])
      family_objects.append(obj)
   
   for i in family_objects:
      print "id = " + str(id(i))
      print i.get_genetic_info()
   print "similar id's says that they are same objects "

if __name__ == '__main__':
   test()

Produzione

Il programma di cui sopra genera il seguente output:

Il modello astratto della fabbrica è anche chiamato fabbrica delle fabbriche. Questo modello di design rientra nella categoria del modello di progettazione creazionale. Fornisce uno dei modi migliori per creare un oggetto.

Include un'interfaccia, responsabile della creazione di oggetti relativi a Factory.

Come implementare il modello di fabbrica astratto?

Il seguente programma aiuta nell'implementazione del pattern factory astratto.

class Window:
   __toolkit = ""
   __purpose = ""

   def __init__(self, toolkit, purpose):
      self.__toolkit = toolkit
      self.__purpose = purpose
   
   def getToolkit(self):
      return self.__toolkit
   
   def getType(self):
      return self.__purpose

class GtkToolboxWindow(Window):
   def __init__(self):
      Window.__init__(self, "Gtk", "ToolboxWindow")

class GtkLayersWindow(Window):
   def __init__(self):
      Window.__init__(self, "Gtk", "LayersWindow")

class GtkMainWindow(Window):
   def __init__(self):
      Window.__init__(self, "Gtk", "MainWindow")

class QtToolboxWindow(Window):
   def __init__(self):
      Window.__init__(self, "Qt", "ToolboxWindow")

class QtLayersWindow(Window):
   def __init__(self):
      Window.__init__(self, "Qt", "LayersWindow")

class QtMainWindow(Window):
   def __init__(self):
      Window.__init__(self, "Qt", "MainWindow")

# Abstract factory class
class UIFactory:
   def getToolboxWindow(self): pass
   def getLayersWindow(self): pass
   def getMainWindow(self): pass

class GtkUIFactory(UIFactory):
   def getToolboxWindow(self):
      return GtkToolboxWindow()
   def getLayersWindow(self):
      return GtkLayersWindow()
   def getMainWindow(self):
      return GtkMainWindow()

class QtUIFactory(UIFactory):
   def getToolboxWindow(self):
      return QtToolboxWindow()
   def getLayersWindow(self):
      return QtLayersWindow()
   def getMainWindow(self):
      return QtMainWindow()

if __name__ == "__main__":
   gnome = True
   kde = not gnome
   
   if gnome:
      ui = GtkUIFactory()
   elif kde:
      ui = QtUIFactory()
   
   toolbox = ui.getToolboxWindow()
   layers = ui.getLayersWindow()
   main = ui.getMainWindow()
   
   print "%s:%s" % (toolbox.getToolkit(), toolbox.getType())
   print "%s:%s" % (layers.getToolkit(), layers.getType())
   print "%s:%s" % (main.getToolkit(), main.getType())

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Nel programma sopra, la fabbrica astratta crea oggetti per ogni finestra. Richiama ogni metodo, che esegue l'output come previsto.

Il pattern orientato agli oggetti è il pattern più comunemente usato. Questo modello può essere trovato in quasi tutti i linguaggi di programmazione.

Come implementare il pattern orientato agli oggetti?

Vediamo ora come implementare il pattern orientato agli oggetti.

class Parrot:
   # class attribute
   species = "bird"
	
   # instance attribute
   def __init__(self, name, age):
      self.name = name
      self.age = age
		
# instantiate the Parrot class
blu = Parrot("Blu", 10)
woo = Parrot("Woo", 15)

# access the class attributes
print("Blu is a {}".format(blu.__class__.species))
print("Woo is also a {}".format(woo.__class__.species))

# access the instance attributes
print("{} is {} years old".format( blu.name, blu.age))
print("{} is {} years old".format( woo.name, woo.age))

Produzione

Il programma precedente genera il seguente output

Spiegazione

Il codice include l'attributo di classe e gli attributi di istanza, che vengono stampati secondo il requisito dell'output. Ci sono varie caratteristiche che fanno parte del modello orientato agli oggetti. Le caratteristiche sono spiegate nel prossimo capitolo.

In questo capitolo, ci concentreremo sui modelli che utilizzano concetti orientati agli oggetti e la loro implementazione in Python. Quando progettiamo i nostri programmi attorno a blocchi di istruzioni, che manipolano i dati attorno alle funzioni, si parla di programmazione orientata alle procedure. Nella programmazione orientata agli oggetti, ci sono due istanze principali chiamate classi e oggetti.

Come implementare classi e variabili oggetto?

L'implementazione di classi e variabili oggetto è la seguente:

class Robot:
   population = 0
   
   def __init__(self, name):
      self.name = name
      print("(Initializing {})".format(self.name))
      Robot.population += 1
   
   def die(self):
      print("{} is being destroyed!".format(self.name))
      Robot.population -= 1
      if Robot.population == 0:
         print("{} was the last one.".format(self.name))
      else:
         print("There are still {:d} robots working.".format(
            Robot.population))
   
   def say_hi(self):
      print("Greetings, my masters call me {}.".format(self.name))
   
   @classmethod
   def how_many(cls):
      print("We have {:d} robots.".format(cls.population))
droid1 = Robot("R2-D2")
droid1.say_hi()
Robot.how_many()

droid2 = Robot("C-3PO")
droid2.say_hi()
Robot.how_many()

print("\nRobots can do some work here.\n")

print("Robots have finished their work. So let's destroy them.")
droid1.die()
droid2.die()

Robot.how_many()

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

Questa illustrazione aiuta a dimostrare la natura delle variabili di classe e oggetto.

  • "Popolazione" appartiene alla classe "Robot". Quindi, viene indicato come una variabile di classe o un oggetto.

  • Qui, ci riferiamo alla variabile della classe di popolazione come Robot.population e non come self.population.

Il modello di progettazione dell'iteratore rientra nella categoria dei modelli di progettazione comportamentale. Gli sviluppatori si imbattono nel modello iteratore in quasi tutti i linguaggi di programmazione. Questo modello viene utilizzato in modo tale da aiutare ad accedere agli elementi di una raccolta (classe) in modo sequenziale senza comprendere il design del livello sottostante.

Come implementare il pattern iteratore?

Vedremo ora come implementare il pattern iteratore.

import time

def fib():
   a, b = 0, 1
   while True:
      yield b
      a, b = b, a + b

g = fib()

try:
   for e in g:
      print(e)
      time.sleep(1)

except KeyboardInterrupt:
   print("Calculation stopped")

Produzione

Il programma di cui sopra genera il seguente output:

Se ti concentri sul pattern, la serie di Fibonacci viene stampata con il pattern iteratore. Alla chiusura forzata dell'utente, viene stampato il seguente output:

Spiegazione

Questo codice Python segue il modello iteratore. Qui, gli operatori di incremento vengono utilizzati per avviare il conteggio. Il conteggio termina con la risoluzione forzata da parte dell'utente.

I dizionari sono le strutture di dati, che includono una combinazione di valori chiave. Questi sono ampiamente utilizzati al posto di JSON - JavaScript Object Notation. I dizionari vengono utilizzati per la programmazione API (Application Programming Interface). Un dizionario mappa un insieme di oggetti su un altro insieme di oggetti. I dizionari sono mutabili; questo significa che possono essere modificati come e quando necessario in base ai requisiti.

Come implementare i dizionari in Python?

Il seguente programma mostra l'implementazione di base dei dizionari in Python a partire dalla sua creazione fino alla sua implementazione.

# Create a new dictionary
d = dict() # or d = {}

# Add a key - value pairs to dictionary
d['xyz'] = 123
d['abc'] = 345

# print the whole dictionary
print(d)

# print only the keys
print(d.keys())

# print only values
print(d.values())

# iterate over dictionary
for i in d :
   print("%s %d" %(i, d[i]))
	
# another method of iteration
for index, value in enumerate(d):
   print (index, value , d[value])

# check if key exist 23. Python Data Structure –print('xyz' in d)

# delete the key-value pair
del d['xyz']

# check again
print("xyz" in d)

Produzione

Il programma di cui sopra genera il seguente output:

Note −Ci sono degli svantaggi legati all'implementazione dei dizionari in Python.

Inconveniente

I dizionari non supportano l'operazione di sequenza dei tipi di dati di sequenza come stringhe, tuple ed elenchi. Questi appartengono al tipo di mappatura integrato.

La struttura dati Lists è un tipo di dati versatile in Python, che può essere scritto come un elenco di valori separati da virgole tra parentesi quadre.

Sintassi

Ecco la sintassi di base per la struttura:

List_name = [ elements ];

Se osservi, la sintassi è dichiarata come array con l'unica differenza che gli elenchi possono includere elementi con diversi tipi di dati. Gli array includono elementi dello stesso tipo di dati. Un elenco può contenere una combinazione di stringhe, numeri interi e oggetti. Gli elenchi possono essere utilizzati per l'implementazione di stack e code.

Gli elenchi sono modificabili. Questi possono essere modificati come e quando necessario.

Come implementare gli elenchi?

Il seguente programma mostra le implementazioni degli elenchi:

my_list = ['p','r','o','b','e']
# Output: p
print(my_list[0])

# Output: o
print(my_list[2])

# Output: e
print(my_list[4])

# Error! Only integer can be used for indexing
# my_list[4.0]

# Nested List
n_list = ["Happy", [2,0,1,5]]

# Nested indexing

# Output: a
print(n_list[0][1])

# Output: 5
print(n_list[1][3])

Produzione

Il programma di cui sopra genera il seguente output:

Le funzioni integrate degli elenchi di Python sono le seguenti:

  • Append()- Aggiunge un elemento alla fine della lista.

  • Extend()- Aggiunge elementi dell'elenco a un altro elenco.

  • Insert()- Inserisce un elemento nell'indice definito.

  • Remove()- Elimina l'elemento dall'elenco specificato.

  • Reverse()- Inverte gli elementi in lista.

  • sort() - Aiuta a ordinare gli elementi in ordine cronologico.

Un insieme può essere definito come raccolta non ordinata che è iterabile, modificabile e non vi è inclusione di elementi duplicati in essa. In Python, set class è una notazione di set matematico. Il vantaggio principale dell'utilizzo di un set è che include un metodo altamente ottimizzato per il controllo di elementi specifici.

Python include una categoria separata chiamata set congelati. Questi set sono oggetti immutabili che supportano solo metodi e operatori che producono un risultato richiesto.

Come implementare i set?

Il seguente programma aiuta nell'implementazione dei set:

# Set in Python

# Creating two sets
set1 = set()
set2 = set()

# Adding elements to set1
for i in range(1, 6):
   set1.add(i)

# Adding elements to set2
for i in range(3, 8):
   set2.add(i)

print("Set1 = ", set1)
print("Set2 = ", set2)
print("\n")

# Union of set1 and set2
set3 = set1 | set2# set1.union(set2)
print("Union of Set1 & Set2: Set3 = ", set3)

# Intersection of set1 and set2
set4 = set1 & set2# set1.intersection(set2)
print("Intersection of Set1 & Set2: Set4 = ", set4)
print("\n")

# Checking relation between set3 and set4
if set3 > set4: # set3.issuperset(set4)
   print("Set3 is superset of Set4")
elif set3 < set4: # set3.issubset(set4)
   print("Set3 is subset of Set4")
else : # set3 == set4
   print("Set3 is same as Set4")

# displaying relation between set4 and set3
if set4 < set3: # set4.issubset(set3)
   print("Set4 is subset of Set3")
   print("\n")

# difference between set3 and set4
set5 = set3 - set4
print("Elements in Set3 and not in Set4: Set5 = ", set5)
print("\n")

# checkv if set4 and set5 are disjoint sets
if set4.isdisjoint(set5):
   print("Set4 and Set5 have nothing in common\n")

# Removing all the values of set5
set5.clear()

print("After applying clear on sets Set5: ")
print("Set5 = ", set5)

Produzione

Il programma di cui sopra genera il seguente output:

Il set congelato può essere dimostrato utilizzando il seguente programma:

normal_set = set(["a", "b","c"])

# Adding an element to normal set is fine
normal_set.add("d")

print("Normal Set")
print(normal_set)

# A frozen set
frozen_set = frozenset(["e", "f", "g"])

print("Frozen Set")
print(frozen_set)

Produzione

Il programma di cui sopra genera il seguente output:

Queue è una raccolta di oggetti, che definiscono una struttura dati semplice seguendo le procedure FIFO (Fast In Fast Out) e LIFO (Last In First Out). Le operazioni di inserimento ed eliminazione sono denominateenqueue e dequeue operazioni.

Le code non consentono l'accesso casuale agli oggetti che contengono.

Come implementare la procedura FIFO?

Il seguente programma aiuta nell'implementazione di FIFO:

import Queue

q = Queue.Queue()

#put items at the end of the queue
for x in range(4):
   q.put("item-" + str(x))

#remove items from the head of the queue
while not q.empty():
   print q.get()

Produzione

Il programma di cui sopra genera il seguente output:

Come implementare la procedura LIFO?

Il seguente programma aiuta nell'implementazione della procedura LIFO:

import Queue

q = Queue.LifoQueue()

#add items at the head of the queue
for x in range(4):
   q.put("item-" + str(x))

#remove items from the head of the queue
while not q.empty():
   print q.get()

Produzione

Il programma di cui sopra genera il seguente output:

Cos'è una coda prioritaria?

La coda prioritaria è una struttura di dati del contenitore che gestisce un set di record con le chiavi ordinate per fornire un accesso rapido al record con la chiave più piccola o più grande nella struttura dati specificata.

Come implementare una coda prioritaria?

L'implementazione della coda di priorità è la seguente:

import Queue

class Task(object):
   def __init__(self, priority, name):
      self.priority = priority
      self.name = name
   
   def __cmp__(self, other):
      return cmp(self.priority, other.priority)

q = Queue.PriorityQueue()

q.put( Task(100, 'a not agent task') )
q.put( Task(5, 'a highly agent task') )
q.put( Task(10, 'an important task') )

while not q.empty():
   cur_task = q.get()
	print 'process task:', cur_task.name

Produzione

Il programma di cui sopra genera il seguente output:

La serializzazione delle stringhe è il processo di scrittura di uno stato di un oggetto in un flusso di byte. In python, la libreria "pickle" viene utilizzata per abilitare la serializzazione. Questo modulo include un potente algoritmo per serializzare e deserializzare una struttura di oggetti Python. "Pickling" è il processo di conversione della gerarchia di oggetti Python in flusso di byte e "unpickling" è la procedura inversa.

La dimostrazione del modulo pickle è la seguente:

import pickle

#Here's an example dict
grades = { 'Alice': 89, 'Bob': 72, 'Charles': 87 }

#Use dumps to convert the object to a serialized string
serial_grades = pickle.dumps( grades )
print(serial_grades)

#Use loads to de-serialize an object
received_grades = pickle.loads( serial_grades )
print(received_grades)

Produzione

Il programma di cui sopra genera il seguente output:

La concorrenza è spesso fraintesa come parallelismo. La concorrenza implica la pianificazione del codice indipendente da eseguire in modo sistematico. Questo capitolo si concentra sull'esecuzione della concorrenza per un sistema operativo che utilizza Python.

Il seguente programma aiuta nell'esecuzione della concorrenza per un sistema operativo:

import os
import time
import threading
import multiprocessing

NUM_WORKERS = 4

def only_sleep():
   print("PID: %s, Process Name: %s, Thread Name: %s" % (
      os.getpid(),
      multiprocessing.current_process().name,
      threading.current_thread().name)
   )
   time.sleep(1)

def crunch_numbers():
   print("PID: %s, Process Name: %s, Thread Name: %s" % (
      os.getpid(),
      multiprocessing.current_process().name,
      threading.current_thread().name)
   )
   x = 0
   while x < 10000000:
      x += 1
for _ in range(NUM_WORKERS):
   only_sleep()
end_time = time.time()
print("Serial time=", end_time - start_time)

# Run tasks using threads
start_time = time.time()
threads = [threading.Thread(target=only_sleep) for _ in range(NUM_WORKERS)]
[thread.start() for thread in threads]
[thread.join() for thread in threads]
end_time = time.time()

print("Threads time=", end_time - start_time)

# Run tasks using processes
start_time = time.time()
processes = [multiprocessing.Process(target=only_sleep()) for _ in range(NUM_WORKERS)]
[process.start() for process in processes]
[process.join() for process in processes]
end_time = time.time()

print("Parallel time=", end_time - start_time)

Produzione

Il programma di cui sopra genera il seguente output:

Spiegazione

"Multiprocessing" è un pacchetto simile al modulo threading. Questo pacchetto supporta la concorrenza locale e remota. Grazie a questo modulo, i programmatori ottengono il vantaggio di utilizzare più processi su un determinato sistema.

Gli anti-pattern seguono una strategia in opposizione ai modelli di design predefiniti. La strategia include approcci comuni a problemi comuni, che possono essere formalizzati e possono essere generalmente considerati come una buona pratica di sviluppo. Di solito, gli anti-pattern sono opposti e indesiderabili. Gli anti-pattern sono determinati modelli utilizzati nello sviluppo del software, che sono considerati cattive pratiche di programmazione.

Caratteristiche importanti degli anti-pattern

Vediamo ora alcune importanti caratteristiche degli anti-modelli.

Correttezza

Questi schemi rompono letteralmente il codice e ti fanno fare cose sbagliate. Di seguito è riportato un semplice esempio di questo:

class Rectangle(object):
def __init__(self, width, height):
self._width = width
self._height = height
r = Rectangle(5, 6)
# direct access of protected member
print("Width: {:d}".format(r._width))

Manutenibilità

Si dice che un programma sia mantenibile se è facile da capire e modificare secondo il requisito. L'importazione del modulo può essere considerata un esempio di manutenibilità.

import math
x = math.ceil(y)
# or
import multiprocessing as mp
pool = mp.pool(8)

Esempio di anti-pattern

L'esempio seguente aiuta nella dimostrazione di anti-pattern -

#Bad
def filter_for_foo(l):
   r = [e for e in l if e.find("foo") != -1]
   if not check_some_critical_condition(r):
      return None
   return r

res = filter_for_foo(["bar","foo","faz"])

if res is not None:
   #continue processing
   pass

#Good
def filter_for_foo(l):
   r = [e for e in l if e.find("foo") != -1]
   if not check_some_critical_condition(r):
      raise SomeException("critical condition unmet!")
   return r

try:
   res = filter_for_foo(["bar","foo","faz"])
   #continue processing

except SomeException:
   i = 0
while i < 10:
   do_something()
   #we forget to increment i

Spiegazione

L'esempio include la dimostrazione di standard buoni e cattivi per la creazione di una funzione in Python.

La gestione delle eccezioni è anche un criterio primario dei modelli di progettazione. Un'eccezione è un errore che si verifica durante l'esecuzione di un programma. Quando si verifica un errore particolare, è importante generare un'eccezione. Questo aiuta a frenare i crash del programma.

Perché utilizzare le eccezioni?

Le eccezioni sono modi convenienti per gestire errori e condizioni speciali in un programma. Quando un utente pensa che il codice specificato possa produrre un errore, è importante utilizzare la gestione delle eccezioni.

Esempio: divisione per zero

import sys

randomList = ['a', 0, 2]

for entry in randomList:
   try:
      print("The entry is", entry)
      r = 1/int(entry)
      break
   except:
      print("Oops!",sys.exc_info()[0],"occured.")
      print("Next entry.")
      print()
print("The reciprocal of",entry,"is",r)

Produzione

Il programma di cui sopra genera il seguente output:

Sollevare eccezioni

Nella programmazione Python in particolare, vengono sollevate eccezioni quando si verifica un errore di codice corrispondente in fase di esecuzione. Questo può essere sollevato con forza usando il“raise” parola chiave.

Sintassi

raise KeyboardInterrupt
Traceback (most recent call last):
...
KeyboardInterrupt