Factory метод для объекта python - лучшая практика
Это вопрос, касающийся наилучшей практики создания экземпляра класса или типа из разных форм одних и тех же данных с использованием python. Лучше ли использовать метод класса или лучше использовать отдельную функцию вообще? Допустим, у меня есть класс, используемый для описания размера документа. (Примечание: Это просто пример. Я хочу знать, как лучше всего создать экземпляр класса NOT, лучший способ описать размер документа.)
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
Мой метод __init__
принимает значение размера, представленное в битах (бит и только биты, и я хочу сохранить его таким образом), но позволяет сказать, что у меня есть значение размера в байтах, и я хочу создать экземпляр моего класса. Лучше ли использовать метод класса или лучше использовать отдельную функцию вообще?
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)
ИЛИ
def create_instance_from_bytes(bytes):
bits = bytes * Size.BYTE
return Size(bits)
Это может показаться не проблемой, и, возможно, оба примера действительны, но я думаю об этом каждый раз, когда мне нужно реализовать что-то вроде этого. В течение долгого времени я предпочел подход метода класса, потому что мне нравятся организационные преимущества связывания класса и метода factory. Кроме того, использование метода класса сохраняет возможность создавать экземпляры любых подклассов, чтобы они больше ориентировались на объекты. С другой стороны, друг однажды сказал: "Когда сомневаешься, делай то, что делает стандартная библиотека", и я еще не нашел пример этого в стандартной библиотеке.
Любая обратная связь очень ценится.
Приветствия
Ответы
Ответ 1
Во-первых, большую часть времени, когда вы думаете, что вам нужно что-то подобное, вы этого не делаете; это признак того, что вы пытаетесь рассматривать Python как Java, и решение состоит в том, чтобы отступить и спросить, зачем вам нужен factory.
Часто самое простое - просто создать конструктор с аргументами default/optional/keyword. Даже случаи, которые вы никогда не напишете в Java, даже случаи, когда перегруженные конструкторы будут чувствовать себя не правильно в С++ или ObjC, могут казаться совершенно естественными в Python. Например, size = Size(bytes=20)
или size = Size(20, Size.BYTES)
выглядят разумно. В этом случае класс Bytes(20)
, который наследует от Size
и добавляет абсолютно ничего, но перегрузка __init__
выглядит разумной. И это тривиально:
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):
Или:
BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
Но иногда вам нужны функции factory. Итак, что вы тогда делаете? Ну, есть два типа вещей, которые часто объединяются в "фабрики".
A @classmethod
- это идиоматический способ выполнения "альтернативного конструктора" - есть примеры по всем stdlib- itertools.chain.from_iterable
, datetime.datetime.fromordinal
и т.д.
Функция - это идиоматический способ сделать "Мне все равно, что такое фактический класс" factory. Посмотрите, например, на встроенную функцию open
. Знаете ли вы, что он возвращает в 3.3? Вы заботитесь? Неа. Вот почему это функция, а не io.TextIOWrapper.open
или что-то еще.
Ваш данный пример кажется вполне законным прецедентом и очень четко вписывается в бит альтернативного конструктора (если он не вписывается в "конструктор с дополнительными аргументами" bin).