Logotipo de Zephyrnet

Patrones de diseño específicos de Python

Fecha:

Introducción

Hasta ahora, hemos cubierto Creacional, Estructuraly Salud de Comportamiento patrones de diseño. Estos pilares fundamentales han ofrecido información sobre cómo crear aplicaciones Python elegantes, fáciles de mantener y escalables. Sin embargo, a medida que profundizamos en los matices de Python, surgen algunos patrones de diseño que son exclusivos del lenguaje mismo: los patrones de diseño específicos de Python.

La sintaxis expresiva y la naturaleza dinámica de Python han llevado al nacimiento de ciertos patrones que podrían no ser tan frecuentes o incluso no existir en otros lenguajes de programación. Estos patrones abordan desafíos específicos del desarrollo de Python y ofrecen a los desarrolladores una forma más pitónica de resolver problemas.

En este último artículo de nuestra serie de patrones de diseño, profundizaremos en los siguientes patrones:

Patrón de objeto global

Al desarrollar aplicaciones, especialmente aquellas de considerable complejidad, a menudo nos encontramos en escenarios en los que necesitamos compartir el estado de un objeto en diferentes partes del sistema. Si bien las variables globales pueden servir para este propósito, generalmente están mal vistas debido a las complicaciones e imprevisibilidad que pueden introducir.

En su lugar, el Patrón de objeto global presenta una solución más controlada y elegante a este dilema. En esencia, este patrón tiene como objetivo proporcionar una instancia compartida singular de un objeto en toda la aplicación, asegurando que el estado permanezca consistente y sincronizado.

Imagine que está diseñando un sistema de registro para una aplicación. Es fundamental que el registrador mantenga configuraciones consistentes (como niveles de registro o formatos de salida) en varios módulos y componentes. En lugar de crear nuevas instancias de registrador o pasar el registrador, sería beneficioso tener una única instancia de registrador accesible globalmente que mantenga las configuraciones compartidas.

El patrón de objeto global normalmente aprovecha la Patrón Singleton (que explicamos anteriormente en esta lección) para garantizar que una clase tenga solo una instancia y proporcione un punto global para acceder a ella. La principal ventaja de utilizar este patrón es la control y previsibilidad Ofrece. Los cambios realizados en el objeto global desde un módulo se reflejarán en todos los demás, asegurando un comportamiento sincronizado.

Creemos el registrador global a partir de nuestro ejemplo usando el patrón de objeto global:

class GlobalLogger: _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(GlobalLogger, cls).__new__(cls, *args, **kwargs) return cls._instance def __init__(self): self.log_level = "INFO" def set_log_level(self, level): self.log_level = level def log(self, message): print(f"[{self.log_level}] - {message}")

Aquí, GlobalLogger siempre devolverá la misma instancia, asegurando que el estado de configuración sea consistente en toda la aplicación:

logger1 = GlobalLogger()
logger1.log("This is an info message.") logger2 = GlobalLogger()
logger2.set_log_level("ERROR")
logger2.log("This is an error message.") logger1.log("This message also shows as an error.") 

Esto nos dará:

[INFO] - This is an info message.
[ERROR] - This is an error message.
[ERROR] - This message also shows as an error.

Patrón de método preenlazado

Uno de los aspectos atractivos de la naturaleza dinámica de Python es su capacidad para crear y manipular funciones y métodos en tiempo de ejecución. A menudo, necesitamos métodos que, cuando se llaman, se comporten de acuerdo con un contexto o datos específicos con los que se asociaron inicialmente.

Aquí es donde entra en juego la Patrón de método preenlazado entra en juego. Nos permite vincular un método a algunos datos o contexto con anticipación, de modo que cuando finalmente se llame al método, conozca inherentemente su contexto sin que se le indique explícitamente.

Piense en un sistema controlado por eventos, como un conjunto de herramientas GUI, donde diferentes componentes de la UI desencadenan acciones específicas cuando interactúan con ellos. Suponga que tiene un conjunto de botones y que cada botón, al hacer clic, debe mostrar su etiqueta.

En lugar de crear métodos separados para cada botón, puede usar un solo método pero vincularlo previamente a los datos del botón respectivo, permitiendo que el método "sepa" inherentemente qué botón lo activó y qué etiqueta debe mostrar.

El patrón del método preenlazado se centra en vincular métodos a datos o contexto específicos mucho antes de la ejecución del método. El método, una vez vinculado, no necesita que se le pase un contexto explícito durante la invocación; en cambio, opera con los datos preconfigurados, lo que garantiza una interacción elegante y fluida.

Veamos cómo funciona esto en acción. Crearemos el Button clase que contiene la etiqueta y un método que maneja los clics. Cuando se hace clic en el botón, se imprime su etiqueta:

class Button: def __init__(self, label): self.label = label self.click_action = lambda: self.display_label(self) def display_label(self, bound_button): print(f"Button pressed: {bound_button.label}") def click(self): self.click_action()

Para probar esto, creemos dos botones diferentes y hagamos clic en cada uno de ellos:

buttonA = Button("Submit")
buttonB = Button("Cancel") buttonA.click()
buttonB.click()

Como era de esperar, al hacer clic en cada botón se produjo el resultado adecuado:

Button pressed: Submit
Button pressed: Cancel

Al permitir que los métodos sean íntimamente conscientes de su contexto antes de la invocación, el patrón de método preligado agiliza las llamadas a métodos y ofrece un enfoque intuitivo para acciones específicas del contexto.

Patrón de objeto centinela

En el desarrollo de software, a veces nos enfrentamos al desafío de distinguir entre los ausencia de un valor y un valor que realmente esté establecido en None o algún otro valor predeterminado. Puede que no sea suficiente confiar simplemente en los valores predeterminados típicos.

El Patrón de objeto centinela ofrece una solución a este dilema. Al crear un objeto único e inconfundible que sirva como centinela, podemos diferenciar entre valores genuinamente ausentes y valores predeterminados.

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Considere un sistema de almacenamiento en caché donde los usuarios puedan almacenar y recuperar valores. Hay un desafío: ¿cómo se diferencia entre una clave que nunca se ha configurado y una clave que se establece con un valor de None¿Y una clave que ha sido expulsada del caché? En tal escenario, simplemente regresar None para una clave faltante puede ser ambiguo. Es None ¿El valor real asociado con la clave, o la clave no existe en absoluto en el caché? Al aprovechar el patrón de objetos Sentinel, podemos brindar claridad en estas situaciones.

El patrón de objetos Sentinel gira en torno a la creación de un objeto único que no se puede confundir con ningún dato legítimo de su aplicación. Este objeto se convierte en la señal inequívoca de que se ha cumplido una condición particular, como un valor faltante:


MISSING = object() class Cache: def __init__(self): self._storage = {} def set(self, key, value): self._storage[key] = value def get(self, key): return self._storage.get(key, MISSING)

Ahora diferenciamos los faltantes y None valores. Cuando agregamos un objeto con None como un valor para un Cache objeto, podremos encontrarlo buscándolo usando su clave:


cache = Cache()
cache.set("username", None) result = cache.get("username")
if result is MISSING: print("Key not found in cache!")
else: print(f"Found value: {result}")

Esto generará el valor del objeto cuya clave es username:

Found value: None

Por otro lado, no podremos encontrar un objeto inexistente:

missing_result = cache.get("non_existent_key")
if missing_result is MISSING: print("Key not found in cache!")

Esto nos dará:

Key not found in cache!

El patrón de objetos Sentinel proporciona una manera clara de representar valores faltantes o de casos especiales, lo que garantiza que su código permanezca inequívoco y fácil de entender.

Conclusión

En este artículo, desenterramos tres patrones distintivos: el Patrón de objeto global, la Patrón de método preenlazado, y la Patrón de objeto centinela. Cada uno de estos patrones aborda desafíos y escenarios exclusivos de la programación en Python.

El Patrón de objeto global subraya el sistema de módulos flexible de Python y el poder de los singletons en la gestión estatal. El Patrón de método preenlazado Resuelve elegantemente los desafíos relacionados con la vinculación de métodos a objetos de clase o instancia, destacando las capacidades orientadas a objetos de Python. Mientras tanto, el Patrón de objeto centinela muestra el dinamismo de Python, proporcionando una poderosa herramienta para señalar casos especiales o comportamientos predeterminados.

Los ejemplos del mundo real que los acompañan no solo ayudan a ilustrar las aplicaciones de estos patrones en la vida real, sino que también hacen que su implementación en Python sea más tangible. Después de leer este artículo, debería poder cerrar la brecha entre la comprensión conceptual y la aplicación práctica de los patrones de diseño específicos de Python.

punto_img

Información más reciente

punto_img