method - static class python
Método de fábrica para los objetos: ¿mejores prácticas? (1)
Primero, la mayoría de las veces crees que necesitas algo como esto, no es así; es una señal de que estás tratando de tratar a Python como a Java, y la solución es dar un paso atrás y preguntar por qué necesitas una fábrica.
A menudo, lo más sencillo es tener un constructor con argumentos predeterminados / opcional / por palabra clave. Incluso los casos que nunca escribirías de esa manera en Java, incluso los casos en que los constructores sobrecargados se sentirían mal en C ++ u ObjC, pueden parecer perfectamente naturales en Python. Por ejemplo, size = Size(bytes=20)
, o size = Size(20, Size.BYTES)
parecen razonables. Para el caso, una clase de Bytes(20)
que se hereda de Size
y no agrega absolutamente nada más que una sobrecarga de __init__
parece razonable. Y estos son triviales para definir:
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):
O:
BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
Pero, a veces necesitas funciones de fábrica. ¿Entonces que haces? Bueno, hay dos tipos de cosas que a menudo se agrupan en "fábricas".
A @classmethod
es la forma idiomática de hacer un "constructor alternativo"; hay ejemplos en toda la plantilla: itertools.chain.from_iterable
, datetime.datetime.fromordinal
, etc.
Una función es la forma idiomática de hacer una fábrica de "No me importa cuál sea la clase real". Mira, por ejemplo, la función open
incorporada. ¿Sabes lo que devuelve en 3.3? ¿Te importa? No Es por eso que es una función, no io.TextIOWrapper.open
o lo que sea.
Su ejemplo dado parece un caso de uso perfectamente legítimo, y encaja bastante claramente en el contenedor del "constructor alternativo" (si no encaja en el contenedor del "constructor con argumentos adicionales").
Esta es una pregunta relacionada con las mejores prácticas para crear una instancia de una clase o tipo a partir de diferentes formas de los mismos datos utilizando Python. ¿Es mejor usar un método de clase o es mejor usar una función separada por completo? Digamos que tengo una clase usada para describir el tamaño de un documento. (Nota: esto es simplemente un ejemplo. Quiero saber la mejor manera de crear una instancia de la clase, no la mejor manera de describir el tamaño de un documento)
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
def __init__(self, bits):
self._bits = bits
@property
def bits(self):
return float(self._bits)
@property
def bytes(self):
return self.bits / self.BYTE
@property
def kilobits(self):
return self.bits / self.KILO
@property
def kilobytes(self):
return self.bytes / self.KILO
@property
def megabits(self):
return self.kilobits / self.KILO
@property
def megabytes(self):
return self.kilobytes / self.KILO
Mi método __init__
toma un valor de tamaño representado en bits (bits y solo bits y quiero mantenerlo de esa manera), pero digamos que tengo un valor de tamaño en bytes y quiero crear una instancia de mi clase. ¿Es mejor usar un método de clase o es mejor usar una función separada por completo?
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
@classmethod
def from_bytes(cls, bytes):
bits = bytes * cls.BYTE
return cls(bits)
O
def create_instance_from_bytes(bytes):
bits = bytes * Size.BYTE
return Size(bits)
Esto puede no parecer un problema y quizás ambos ejemplos son válidos, pero pienso en ello cada vez que necesito implementar algo como esto. Por mucho tiempo he preferido el enfoque de método de clase porque me gustan los beneficios organizacionales de unir la clase y el método de fábrica. Además, el uso de un método de clase preserva la capacidad de crear instancias de cualquier subclase para que esté más orientado a objetos. Por otro lado, un amigo dijo una vez "En caso de duda, haga lo que hace la biblioteca estándar" y todavía tengo que encontrar un ejemplo de esto en la biblioteca estándar.