pattern method decorators python coding-style decorator class-method factory-method

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.