Patrones de diseño de Python - Guía rápida
Los patrones de diseño se utilizan para representar el patrón utilizado por los desarrolladores para crear software o aplicación web. Estos patrones se seleccionan en función del análisis de requisitos. Los patrones describen la solución al problema, cuándo y dónde aplicar la solución y las consecuencias de la implementación.
Estructura de un patrón de diseño
La documentación del patrón de diseño se mantiene de una manera que se enfoca más en la tecnología que se usa y de qué manera. El siguiente diagrama explica la estructura básica de la documentación de patrones de diseño.
Nombre del patrón
Describe el patrón de manera breve y eficaz.
Intención / Motivo
Describe lo que hace el patrón.
Aplicabilidad
Describe la lista de situaciones en las que se aplica el patrón.
Participantes y consecuencias
Los participantes incluyen clases y objetos que participan en el patrón de diseño con una lista de consecuencias que existen con el patrón.
¿Por qué Python?
Python es un lenguaje de programación de código abierto. Tiene bibliotecas que admiten una variedad de patrones de diseño. La sintaxis de Python es fácil de entender y utiliza palabras clave en inglés.
Python proporciona soporte para la lista de patrones de diseño que se mencionan a continuación. Estos patrones de diseño se utilizarán a lo largo de este tutorial:
- Patrón de controlador de vista de modelo
- Patrón singleton
- Patrón de fábrica
- Patrón de constructor
- Patrón de prototipo
- Patrón de fachada
- Patrón de comando
- Patrón de adaptador
- Patrón de prototipo
- Patrón de decorador
- Patrón de proxy
- Patrón de cadena de responsabilidad
- Patrón de observador
- Patrón de estado
- Patrón de estrategia
- Patrón de plantilla
- Patrón de peso mosca
- Patrón de fábrica abstracto
- Patrón orientado a objetos
Beneficios de usar un patrón de diseño
A continuación se muestran los diferentes beneficios del patrón de diseño:
Los patrones proporcionan al desarrollador una selección de soluciones probadas y comprobadas para los problemas especificados.
Todos los patrones de diseño tienen un lenguaje neutro.
Los patrones ayudan a lograr la comunicación y mantener una buena documentación.
Incluye un historial de logros para reducir cualquier riesgo técnico del proyecto.
Los patrones de diseño son muy flexibles de usar y fáciles de entender.
Python es un lenguaje de programación de código abierto, que es de alto nivel, interpretado, interactivo y orientado a objetos. Está diseñado para ser muy legible. La sintaxis del lenguaje Python es fácil de entender y utiliza palabras clave en inglés con frecuencia.
Características del lenguaje Python
En esta sección, aprenderemos sobre las diferentes características del lenguaje Python.
Interpretado
Python se procesa en tiempo de ejecución mediante el intérprete. No es necesario compilar el programa antes de su ejecución. Es similar a PERL y PHP.
Orientado a objetos
Python sigue patrones de diseño y estilo orientados a objetos. Incluye definición de clases con varias características como encapsulación, polimorfismo y muchas más.
Portátil
Código Python escrito en el sistema operativo Windows y se puede utilizar en el sistema operativo Mac. El código se puede reutilizar y portátil según los requisitos.
Fácil de codificar
La sintaxis de Python es fácil de entender y codificar. Cualquier desarrollador puede comprender la sintaxis de Python en pocas horas. Python se puede describir como "apto para programadores"
Extensible
Si es necesario, un usuario también puede escribir parte del código Python en lenguaje C. También es posible poner código Python en código fuente en diferentes lenguajes como C ++. Esto convierte a Python en un lenguaje extensible.
Puntos importantes
Considere los siguientes puntos importantes relacionados con el lenguaje de programación Python:
Incluye métodos de programación funcionales y estructurados, así como métodos de programación orientados a objetos.
Puede usarse como lenguaje de scripting o como lenguaje de programación.
Incluye recolección automática de basura.
Incluye tipos de datos dinámicos de alto nivel y admite varias comprobaciones de tipos dinámicos.
Python incluye una función de integración con C, C ++ y lenguajes como Java.
¿Cómo descargar el lenguaje Python en su sistema?
Para descargar el lenguaje Python en su sistema, siga este enlace:
https://www.python.org/downloads/Incluye paquetes para varios sistemas operativos como distribuciones de Windows, MacOS y Linux.
Las herramientas importantes en Python
En esta sección, aprenderemos brevemente sobre algunas herramientas importantes en Python.
Cadenas de Python
La declaración básica de cadenas es la siguiente:
str = 'Hello World!'
Listas de Python
Las listas de Python se pueden declarar como tipos de datos compuestos separados por comas y encerrados entre corchetes ([]).
list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
tinylist = [123, 'john']
Tuplas de Python
Una tupla es un tipo de datos dinámicos de Python, que consta de un número de valores separados por comas. Las tuplas están entre paréntesis.
tinytuple = (123, 'john')
Diccionario de Python
El diccionario de Python es un tipo de tabla hash. Una clave de diccionario puede ser casi cualquier tipo de datos de Python. Los tipos de datos suelen ser números o cadenas.
tinydict = {'name': 'omkar','code':6734, 'dept': 'sales'}
¿Qué constituye un patrón de diseño en Python?
Python ayuda a constituir un patrón de diseño utilizando los siguientes parámetros:
- Nombre del patrón
- Intent
- Aliases
- Motivation
- Problem
- Solution
- Structure
- Participants
- Constraints
- Código de muestra
El controlador de vista de modelo es el patrón de diseño más utilizado. A los desarrolladores les resulta fácil implementar este patrón de diseño.
A continuación se muestra una arquitectura básica del controlador de vista de modelo:
Veamos ahora cómo funciona la estructura.
Modelo
Consiste en una lógica de aplicación pura, que interactúa con la base de datos. Incluye toda la información para representar datos al usuario final.
Ver
Ver representa los archivos HTML, que interactúan con el usuario final. Representa los datos del modelo al usuario.
Controlador
Actúa como intermediario entre vista y modelo. Escucha los eventos desencadenados por la vista y el modelo de consultas para los mismos.
Código Python
Consideremos un objeto básico llamado "Persona" y creemos un patrón de diseño 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
Requiere un método, que recupera todos los registros de la tabla Person en la base de datos. Los registros se presentan en formato JSON.
Ver
Muestra todos los registros obtenidos dentro del modelo. La vista nunca interactúa con el modelo; El controlador hace este trabajo (comunicándose con el modelo y 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!'
Controlador
El controlador interactúa con el modelo a través del getAll() método que recupera todos los registros que se muestran al usuario final.
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()
Este patrón restringe la instanciación de una clase a un objeto. Es un tipo de patrón de creación e involucra solo una clase para crear métodos y objetos específicos.
Proporciona un punto de acceso global a la instancia creada.
¿Cómo implementar una clase singleton?
El siguiente programa demuestra la implementación de la clase singleton donde imprime las instancias creadas varias veces.
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
Salida
El programa anterior genera la siguiente salida:
El número de instancias creadas es el mismo y no hay diferencia en los objetos enumerados en la salida.
El patrón de fábrica se incluye en la categoría de lista de patrones de creación. Proporciona una de las mejores formas de crear un objeto. En el patrón de fábrica, los objetos se crean sin exponer la lógica al cliente y haciendo referencia al objeto recién creado utilizando una interfaz común.
Los patrones de fábrica se implementan en Python utilizando el método de fábrica. Cuando un usuario llama a un método tal que le pasamos una cadena y el valor de retorno como un nuevo objeto se implementa a través del método de fábrica. El tipo de objeto utilizado en el método de fábrica está determinado por la cadena que se pasa a través del método.
En el siguiente ejemplo, cada método incluye un objeto como parámetro, que se implementa a través del método de fábrica.
¿Cómo implementar un patrón de fábrica?
Veamos ahora cómo implementar un patrón de fábrica.
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 clase de botón ayuda a crear las etiquetas html y la página html asociada. El cliente no tendrá acceso a la lógica del código y la salida representa la creación de una página html.
Salida
Explicación
El código de Python incluye la lógica de las etiquetas html, que especifica el valor. El usuario final puede echar un vistazo al archivo HTML creado por el código Python.
Builder Pattern es un patrón de diseño único que ayuda a construir objetos complejos utilizando objetos simples y utiliza un enfoque algorítmico. Este patrón de diseño pertenece a la categoría de patrón de creación. En este patrón de diseño, una clase de constructor crea el objeto final en un procedimiento paso a paso. Este constructor es independiente de otros objetos.
Ventajas de Builder Pattern
Proporciona una separación clara y una capa única entre la construcción y la representación de un objeto específico creado por clase.
Proporciona un mejor control sobre el proceso de construcción del patrón creado.
Ofrece el escenario perfecto para cambiar la representación interna de los objetos.
¿Cómo implementar el patrón de constructor?
En esta sección, aprenderemos cómo implementar el patrón de construcción.
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()
Salida
El programa anterior genera la siguiente salida:
El patrón de diseño de prototipos ayuda a ocultar la complejidad de las instancias creadas por la clase. El concepto del objeto existente será diferente al del nuevo objeto, que se crea desde cero.
El objeto recién copiado puede tener algunos cambios en las propiedades si es necesario. Este enfoque ahorra tiempo y recursos que se emplean en el desarrollo de un producto.
¿Cómo implementar un patrón prototipo?
Veamos ahora cómo implementar un patrón 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()
Salida
El programa anterior generará la siguiente salida:
La salida ayuda a crear nuevos objetos con los existentes y es claramente visible en la salida mencionada anteriormente.
El patrón de diseño de fachada proporciona una interfaz unificada a un conjunto de interfaces en un subsistema. Define una interfaz de nivel superior que puede utilizar cualquier subsistema.
Una clase de fachada sabe qué subsistema es responsable de una solicitud.
¿Cómo diseñar un patrón de fachada?
Veamos ahora cómo diseñar un patrón de fachada.
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()
Salida
El programa anterior genera la siguiente salida:
Explicación
Este programa está diseñado con un escenario. Es el de poner en marcha el motor de un automóvil o de cualquier vehículo que lo conduzca. Si observa el código, incluye las funciones asociadas para conducir, estacionar y consumir combustible.
Command Pattern agrega un nivel de abstracción entre acciones e incluye un objeto, que invoca estas acciones.
En este patrón de diseño, el cliente crea un objeto de comando que incluye una lista de comandos a ejecutar. El objeto de comando creado implementa una interfaz específica.
A continuación se muestra la arquitectura básica del patrón de comando:
¿Cómo implementar el patrón de comando?
Ahora veremos cómo implementar el patrón de diseño.
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)
Salida
El programa anterior genera la siguiente salida:
Explicación
La salida implementa todos los comandos y palabras clave enumerados en el lenguaje Python. Imprime los valores necesarios de las variables.
El patrón de adaptador funciona como un puente entre dos interfaces incompatibles. Este tipo de patrón de diseño viene bajo patrón estructural ya que este patrón combina la capacidad de dos interfaces independientes.
Este patrón involucra una sola clase, que se encarga de unir funcionalidades de interfaces independientes o incompatibles. Un ejemplo de la vida real podría ser el caso de un lector de tarjetas, que actúa como adaptador entre la tarjeta de memoria y una computadora portátil. Conecta la tarjeta de memoria en el lector de tarjetas y el lector de tarjetas en la computadora portátil para que la tarjeta de memoria se pueda leer a través de la computadora portátil.
El patrón de diseño del adaptador ayuda a que las clases trabajen juntas. Convierte la interfaz de una clase en otra interfaz según los requisitos. El patrón incluye una especiación, un polimorfismo que nombra un nombre y múltiples formas. Digamos por una clase de forma que se pueda usar según los requisitos reunidos.
Hay dos tipos de patrones de adaptadores:
Patrón de adaptador de objeto
Este patrón de diseño se basa en la implementación de objetos. Por lo tanto, se denomina Patrón de adaptador de objeto.
Patrón de adaptador de clase
Esta es una forma alternativa de implementar el patrón de diseño del adaptador. El patrón se puede implementar usando múltiples herencias.
¿Cómo implementar el patrón de adaptador?
Veamos ahora cómo implementar el patrón de adaptador.
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()
Salida
El programa anterior genera la siguiente salida:
Explicación
El código incluye una interfaz de adaptador con varios parámetros y atributos. Incluye Adaptee junto con la interfaz de Target que implementa todos los atributos y muestra la salida como visible.
El patrón de decorador permite al usuario agregar una nueva funcionalidad a un objeto existente sin alterar su estructura. Este tipo de patrón de diseño se incluye en el patrón estructural, ya que este patrón actúa como un envoltorio para la clase existente.
Este patrón crea una clase decoradora, que envuelve la clase original y proporciona funcionalidad adicional manteniendo intacta la firma de los métodos de clase.
El motivo de un patrón de decorador es adjuntar responsabilidades adicionales de un objeto de forma dinámica.
Cómo implementar el patrón de diseño del decorador
El código mencionado a continuación es una demostración simple de cómo implementar el patrón de diseño del decorador en Python. La ilustración implica la demostración de una cafetería en formato de clase. La clase de café creada es abstracta, lo que significa que no se puede instanciar.
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'
La implementación de la clase abstracta de la cafetería se realiza con un archivo separado como se menciona a continuación:
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()))
Salida
El programa anterior genera la siguiente salida:
El patrón de diseño de proxy incluye un nuevo objeto, que se denomina "Proxy" en lugar de un objeto existente que se denomina "Sujeto real". El objeto proxy creado del sujeto real debe estar en la misma interfaz de tal manera que el cliente no debería tener la menor idea de que se usa un proxy en lugar del objeto real. Las solicitudes generadas por el cliente al proxy se pasan a través del sujeto real.
La representación UML del patrón de proxy es la siguiente:
¿Cómo implementar el patrón de proxy?
Veamos ahora cómo implementar el patrón de 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
Salida
El programa anterior genera la siguiente salida:
El diseño del patrón de proxy ayuda a replicar las imágenes que creamos. La función display_image () ayuda a verificar si los valores se imprimen en el símbolo del sistema.
El patrón de cadena de responsabilidad se utiliza para lograr un acoplamiento flexible en el software donde una solicitud específica del cliente se pasa a través de una cadena de objetos incluidos en él. Ayuda a construir una cadena de objetos. La solicitud entra por un extremo y pasa de un objeto a otro.
Este patrón permite que un objeto envíe un comando sin saber qué objeto manejará la solicitud.
¿Cómo implementar el patrón de cadena de responsabilidad?
Ahora veremos cómo implementar el patrón de cadena de responsabilidad.
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)
Salida
El programa anterior genera la siguiente salida:
Explicación
El código anterior crea un informe para las tareas mensuales donde envía comandos a través de cada función. Se necesitan dos controladores: para PDF y para texto. Imprime la salida una vez que el objeto requerido ejecuta cada función.
En este patrón, los objetos se representan como observadores que esperan a que se active un evento. Un observador se adhiere al sujeto una vez que ocurre el evento especificado. A medida que ocurre el evento, el sujeto les dice a los observadores que ha ocurrido.
El siguiente diagrama UML representa el patrón del observador:
¿Cómo implementar el patrón de observador?
Veamos ahora cómo implementar el patrón de observador.
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()
Salida
El programa anterior genera la siguiente salida:
Explicación
El código anterior explica el procedimiento para descargar un resultado en particular. Según la lógica del patrón del observador, cada objeto se trata como un observador. Imprime la salida cuando se activa el evento.
Proporciona un módulo para máquinas de estado, que se implementan mediante subclases, derivadas de una clase de máquina de estado específica. Los métodos son independientes del estado y provocan transiciones declaradas mediante decoradores.
¿Cómo implementar el patrón estatal?
La implementación básica del patrón de estado se muestra a continuación:
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)
Salida
El programa anterior genera la siguiente salida:
El patrón de estrategia es un tipo de patrón de comportamiento. El objetivo principal del patrón de estrategia es permitir que el cliente elija entre diferentes algoritmos o procedimientos para completar la tarea especificada. Se pueden intercambiar diferentes algoritmos sin complicaciones para la tarea mencionada.
Este patrón se puede utilizar para mejorar la flexibilidad cuando se accede a recursos externos.
¿Cómo implementar el patrón de estrategia?
El programa que se muestra a continuación ayuda a implementar el patrón de estrategia.
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()
Salida
El programa anterior genera la siguiente salida:
Explicación
Proporciona una lista de estrategias de las funciones que ejecutan la salida. El foco principal de este patrón de comportamiento es el comportamiento.
Un patrón de plantilla define un algoritmo básico en una clase base usando una operación abstracta donde las subclases anulan el comportamiento concreto. El patrón de plantilla mantiene el esquema del algoritmo en un método separado. Este método se denomina método de plantilla.
A continuación se muestran las diferentes características del patrón de plantilla:
Define el esqueleto del algoritmo en una operación.
Incluye subclases, que redefinen ciertos pasos de 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()
Salida
El programa anterior genera la siguiente salida:
Explicación
Este código crea una plantilla para preparar comida. Aquí, cada parámetro representa el atributo para crear una parte de la comida como té, pizza, etc.
La salida representa la visualización de atributos.
El patrón de peso mosca se incluye en la categoría de patrones de diseño estructural. Proporciona una forma de reducir el número de objetos. Incluye varias características que ayudan a mejorar la estructura de la aplicación. La característica más importante de los objetos de peso mosca es inmutable. Esto significa que no se pueden modificar una vez construidos. El patrón usa un HashMap para almacenar objetos de referencia.
¿Cómo implementar el patrón de peso mosca?
El siguiente programa ayuda a implementar el patrón de peso 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()
Salida
El programa anterior genera la siguiente salida:
El patrón de fábrica abstracto también se llama fábrica de fábricas. Este patrón de diseño se incluye en la categoría de patrón de diseño de creación. Proporciona una de las mejores formas de crear un objeto.
Incluye una interfaz, que se encarga de crear objetos relacionados con Factory.
¿Cómo implementar el patrón de fábrica abstracto?
El siguiente programa ayuda a implementar el patrón de fábrica abstracto.
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())
Salida
El programa anterior genera la siguiente salida:
Explicación
En el programa anterior, la fábrica abstracta crea objetos para cada ventana. Llama a cada método, que ejecuta la salida como se esperaba.
El patrón orientado a objetos es el patrón más utilizado. Este patrón se puede encontrar en casi todos los lenguajes de programación.
¿Cómo implementar el patrón orientado a objetos?
Veamos ahora cómo implementar el patrón orientado a objetos.
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))
Salida
El programa anterior genera la siguiente salida
Explicación
El código incluye atributos de clase y de instancia, que se imprimen según los requisitos de la salida. Hay varias características que forman parte del patrón orientado a objetos. Las funciones se explican en el siguiente capítulo.
En este capítulo, nos centraremos en los patrones que utilizan conceptos orientados a objetos y su implementación en Python. Cuando diseñamos nuestros programas alrededor de bloques de declaraciones, que manipulan los datos alrededor de funciones, se llama programación orientada a procedimientos. En la programación orientada a objetos, hay dos instancias principales llamadas clases y objetos.
¿Cómo implementar clases y variables de objeto?
La implementación de clases y variables de objeto es la siguiente:
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()
Salida
El programa anterior genera la siguiente salida:
Explicación
Esta ilustración ayuda a demostrar la naturaleza de las variables de clase y objeto.
"Población" pertenece a la clase "Robot". Por lo tanto, se denomina variable de clase u objeto.
Aquí, nos referimos a la variable de clase de población como Robot.population y no como self.population.
El patrón de diseño de iterador se incluye en la categoría de patrones de diseño de comportamiento. Los desarrolladores encuentran el patrón de iterador en casi todos los lenguajes de programación. Este patrón se usa de tal manera que ayuda a acceder a los elementos de una colección (clase) de manera secuencial sin comprender el diseño de la capa subyacente.
¿Cómo implementar el patrón iterador?
Ahora veremos cómo implementar el patrón de iterador.
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")
Salida
El programa anterior genera la siguiente salida:
Si se centra en el patrón, la serie de Fibonacci se imprime con el patrón iterador. Tras la terminación forzosa del usuario, se imprime la siguiente salida:
Explicación
Este código de Python sigue el patrón de iterador. Aquí, los operadores de incremento se utilizan para iniciar el recuento. El conteo termina con la rescisión forzosa por parte del usuario.
Los diccionarios son las estructuras de datos, que incluyen una combinación de valores clave. Estos se utilizan ampliamente en lugar de JSON - JavaScript Object Notation. Los diccionarios se utilizan para la programación API (Interfaz de programación de aplicaciones). Un diccionario asigna un conjunto de objetos a otro conjunto de objetos. Los diccionarios son mutables; esto significa que se pueden cambiar cuando sea necesario según los requisitos.
¿Cómo implementar diccionarios en Python?
El siguiente programa muestra la implementación básica de diccionarios en Python desde su creación hasta su implementación.
# 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)
Salida
El programa anterior genera la siguiente salida:
Note −Existen inconvenientes relacionados con la implementación de diccionarios en Python.
Retirarse
Los diccionarios no admiten la operación de secuencia de los tipos de datos de secuencia como cadenas, tuplas y listas. Estos pertenecen al tipo de mapeo incorporado.
La estructura de datos Lists es un tipo de datos versátil en Python, que se puede escribir como una lista de valores separados por comas entre corchetes.
Sintaxis
Aquí está la sintaxis básica de la estructura:
List_name = [ elements ];
Si observa, la sintaxis se declara como matrices con la única diferencia de que las listas pueden incluir elementos con diferentes tipos de datos. Las matrices incluyen elementos del mismo tipo de datos. Una lista puede contener una combinación de cadenas, números enteros y objetos. Las listas se pueden utilizar para la implementación de pilas y colas.
Las listas son mutables. Estos se pueden cambiar cuando sea necesario.
¿Cómo implementar listas?
El siguiente programa muestra las implementaciones de listas:
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])
Salida
El programa anterior genera la siguiente salida:
Las funciones integradas de las listas de Python son las siguientes:
Append()- Agrega elemento al final de la lista.
Extend()- Agrega elementos de la lista a otra lista.
Insert()- Inserta un elemento en el índice definido.
Remove()- Elimina el elemento de la lista especificada.
Reverse()- Invierte los elementos de la lista.
sort() - Ayuda a ordenar los elementos en orden cronológico.
Un conjunto se puede definir como una colección desordenada que es iterable, mutable y que no incluye elementos duplicados. En Python, la clase de conjunto es una notación de conjunto matemático. La principal ventaja de usar un conjunto es que incluye un método altamente optimizado para verificar un elemento específico.
Python incluye una categoría separada llamada conjuntos congelados. Estos conjuntos son objetos inmutables que solo admiten métodos y operadores que producen un resultado requerido.
¿Cómo implementar conjuntos?
El siguiente programa ayuda en la implementación de conjuntos:
# 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)
Salida
El programa anterior genera la siguiente salida:
El conjunto congelado se puede demostrar usando el siguiente programa:
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)
Salida
El programa anterior genera la siguiente salida:
Queue es una colección de objetos, que define una estructura de datos simple siguiendo los procedimientos FIFO (Fast In Fast Out) y LIFO (Last In First Out). Las operaciones de inserción y eliminación se denominanenqueue y dequeue operaciones.
Las colas no permiten el acceso aleatorio a los objetos que contienen.
¿Cómo implementar el procedimiento FIFO?
El siguiente programa ayuda en la implementación de 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()
Salida
El programa anterior genera la siguiente salida:
¿Cómo implementar el procedimiento LIFO?
El siguiente programa ayuda en la implementación del procedimiento 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()
Salida
El programa anterior genera la siguiente salida:
¿Qué es una cola de prioridad?
La cola de prioridad es una estructura de datos de contenedor que administra un conjunto de registros con las claves ordenadas para proporcionar acceso rápido al registro con la clave más pequeña o más grande en la estructura de datos especificada.
¿Cómo implementar una cola de prioridad?
La implementación de la cola de prioridad es la siguiente:
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
Salida
El programa anterior genera la siguiente salida:
La serialización de cadenas es el proceso de escribir un estado de objeto en un flujo de bytes. En Python, la biblioteca "pickle" se usa para habilitar la serialización. Este módulo incluye un poderoso algoritmo para serializar y deserializar una estructura de objeto Python. "Decapado" es el proceso de convertir la jerarquía de objetos de Python en un flujo de bytes y "despegar" es el procedimiento inverso.
La demostración del módulo pickle es la siguiente:
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)
Salida
El programa anterior genera la siguiente salida:
La concurrencia a menudo se malinterpreta como paralelismo. La concurrencia implica programar código independiente para que se ejecute de manera sistemática. Este capítulo se centra en la ejecución de la simultaneidad para un sistema operativo que utiliza Python.
El siguiente programa ayuda en la ejecución de la concurrencia para 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)
Salida
El programa anterior genera la siguiente salida:
Explicación
"Multiprocesamiento" es un paquete similar al módulo de subprocesamiento. Este paquete admite simultaneidad local y remota. Debido a este módulo, los programadores obtienen la ventaja de utilizar múltiples procesos en el sistema dado.
Los anti-patrones siguen una estrategia en oposición a los patrones de diseño predefinidos. La estrategia incluye enfoques comunes para problemas comunes, que se pueden formalizar y, en general, se pueden considerar como una buena práctica de desarrollo. Generalmente, los anti-patrones son opuestos e indeseables. Los anti-patrones son ciertos patrones utilizados en el desarrollo de software, que se consideran malas prácticas de programación.
Características importantes de los anti-patrones
Veamos ahora algunas características importantes de los anti-patrones.
Exactitud
Estos patrones literalmente rompen tu código y te hacen hacer cosas incorrectas. A continuación se muestra una ilustración simple de esto:
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))
Mantenibilidad
Se dice que un programa se puede mantener si es fácil de entender y modificar según los requisitos. El módulo de importación se puede considerar como un ejemplo de mantenibilidad.
import math
x = math.ceil(y)
# or
import multiprocessing as mp
pool = mp.pool(8)
Ejemplo de anti-patrón
El siguiente ejemplo ayuda en la demostración de anti-patrones:
#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
Explicación
El ejemplo incluye la demostración de estándares buenos y malos para crear una función en Python.
El manejo de excepciones también es un criterio principal de los patrones de diseño. Una excepción es un error que ocurre durante la ejecución de un programa. Cuando ocurre un error en particular, es importante generar una excepción. Esto ayuda a frenar los bloqueos del programa.
¿Por qué utilizar excepciones?
Las excepciones son formas convenientes de manejar errores y condiciones especiales en un programa. Cuando un usuario piensa que el código especificado puede producir un error, es importante utilizar el manejo de excepciones.
Ejemplo: división por cero
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)
Salida
El programa anterior genera la siguiente salida:
Generación de excepciones
En la programación de Python específicamente, se generan excepciones cuando se produce el error de código correspondiente en tiempo de ejecución. Esto se puede levantar con fuerza utilizando el“raise” palabra clave.
Sintaxis
raise KeyboardInterrupt
Traceback (most recent call last):
...
KeyboardInterrupt