Подтвердить что ты не робот

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. Кроме того, использование метода класса сохраняет возможность создавать экземпляры любых подклассов, чтобы они больше ориентировались на объекты. С другой стороны, друг однажды сказал: "Когда сомневаешься, делай то, что делает стандартная библиотека", и я еще не нашел пример этого в стандартной библиотеке.

Любая обратная связь очень ценится.

Приветствия

4b9b3361

Ответ 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).