HOWTO · Python
Fábrica de clases de Python
Este tutorial trata sobre las fábricas de clases en Python.
En esta página
Una fábrica de clases es un elemento invocable que crea y devuelve un objeto de clase. Llamar a la fábrica no crea una instancia; proporciona una clase que se puede instanciar después. Esta diferencia resulta útil cuando la definición de una clase depende de una configuración disponible solo mientras se ejecuta el programa.
La mayoría de los programas de Python deben definir una clase normal en el nivel del módulo. Es la opción más clara cuando la clase es fija y se usa de una forma predecible. Use una fábrica cuando quien llama necesite una clase adaptada, por ejemplo con un valor predeterminado seleccionado, un nombre generado o métodos compuestos a partir de una configuración pequeña. Mantenga la fábrica enfocada: devolver muchas formas de clase no relacionadas puede dificultar la comprensión y las pruebas de los tipos.
Este tutorial muestra una definición class anidada y la forma de tres argumentos de type() de Python. Ambas técnicas devuelven un objeto de clase. El enfoque de clase anidada suele ser más fácil de leer; type() es útil cuando el nombre de la clase, las clases base o el espacio de nombres deben proporcionarse dinámicamente.
Cómo crear una fábrica de clases en Python
Una fábrica de clases requiere dos llamadas en su uso normal. Primero, llame a la fábrica y guarde la clase devuelta. Segundo, llame a esa clase devuelta para crear un objeto. Por ejemplo, Ball = create_ball_class() asigna una clase a Ball, mientras que Ball("green") construye una instancia de Ball.
La palabra clave class crea la clase cuando Python ejecuta el cuerpo de la función. La forma type(name, bases, namespace) también crea una clase en ese momento: name es una cadena, bases es una tupla de clases padre y namespace asigna nombres de atributos a valores o funciones. Las funciones colocadas en el espacio de nombres se convierten en métodos de instancia al acceder a ellas mediante una instancia.
Elija nombres y argumentos del constructor que hagan predecible la clase devuelta. Si cada llamada a la fábrica produce una clase distinta, las instancias de llamadas separadas no comparten la misma identidad de clase, aunque sus definiciones parezcan idénticas. Esto puede importar para comprobaciones con isinstance(), serialización y depuración. Si necesita un tipo público estable, defínalo una vez con la sintaxis normal de clase y pase la configuración a sus instancias.
Utilice la palabra clave class para crear una fábrica de clases en Python
Coloque una definición de clase dentro de una función y devuelva el nombre de la clase. La clase anidada puede usar valores del ámbito de la fábrica, lo que hace que este patrón sea práctico para pequeñas decisiones de configuración. En este ejemplo, la fábrica devuelve Ball; la instancia guarda el color que se pasó a su constructor.
def create_ball_class():
class Ball:
def __init__(self, color):
self.color = color
def get_color(self):
return self.color
return Ball
Ball = create_ball_class()
ball = Ball("green")
print(ball.get_color())
La clase devuelta se comporta como cualquier otra clase, de modo que sus métodos y atributos están disponibles en la instancia.
green
Para una fábrica que acepta configuración, valídela antes de devolver la clase o en el constructor generado. La validación hace que una entrada no válida falle en un límite documentado, en vez de permitir que circule un objeto inutilizable. El siguiente caso límite rechaza un color vacío y controla la excepción para que el programa tenga una salida determinista.
def create_ball_class(default_color):
class Ball:
def __init__(self, color=default_color):
if not color:
raise ValueError("color must not be empty")
self.color = color
return Ball
Ball = create_ball_class("")
try:
Ball()
except ValueError as error:
print(error)
color must not be empty
Esta fábrica cierra sobre default_color, por lo que cada clase devuelta recuerda el valor proporcionado para esa llamada concreta. Es conveniente para configuraciones pequeñas e inmutables. Evite cerrar sobre objetos compartidos mutables, salvo que el estado compartido sea intencional; de otro modo, una instancia puede afectar inesperadamente a otra. Si el ajuste pertenece a todas las instancias pero debe ser fácil de inspeccionar, asignarlo como atributo de clase es otra opción.
Use la función integrada type() de tres argumentos para crear una fábrica de clases en Python
La función integrada type() puede construir una clase directamente en tiempo de ejecución. Su forma de tres argumentos, type(name, bases, namespace), recibe una cadena para el nombre de la clase, una tupla de clases base y un diccionario para el espacio de nombres. El diccionario contiene atributos de clase; una función almacenada bajo "get_color", por ejemplo, se usa como método de instancia. Una llamada directa es concisa cuando todas esas piezas ya son datos, pero las funciones auxiliares definidas junto a ella siguen siendo nombres de nivel de módulo.
def init(self, color):
self.color = color
def get_color(self):
return self.color
Ball = type(
"Ball",
(object,),
{"__init__": init, "get_color": get_color},
)
ball = Ball("green")
print(ball.get_color())
green
Encapsule las funciones auxiliares en una fábrica cuando sean detalles de implementación y cuando la fábrica deba devolver una clase de tiempo de ejecución configurada. El código siguiente tiene el mismo comportamiento observable, mientras que init y get_color quedan limitadas a la fábrica en vez de añadirse al espacio de nombres del módulo.
def create_ball_class(class_name):
def init(self, color):
self.color = color
def get_color(self):
return self.color
return type(
class_name,
(object,),
{"__init__": init, "get_color": get_color},
)
Ball = create_ball_class("Ball")
ball = Ball("green")
print(Ball.__name__)
print(ball.get_color())
Ball
green
Use este enfoque cuando el nombre, la tupla de herencia o el conjunto de atributos dependan realmente de datos. Para una clase estática, la sintaxis normal class ofrece mejor legibilidad, compatibilidad con comprobadores de tipos y trazas de errores. Elija el enfoque que elija, pruebe la clase devuelta construyendo una instancia y ejercitando el comportamiento que necesita quien llama, en vez de comprobar únicamente que la fábrica devolvió un valor.