Рубрики
Без рубрики

Паттерны дизайна, которые имеют смысл в Python: Простая фабрика

В первом посте этой серии я расскажу о шаблонах дизайна, которые имеют смысл в Python. Мы … с меткой Python, Tutorial, программирование.

В первом посте этой серии я расскажу о шаблонах дизайна, которые имеют смысл в Python. Мы посмотрим, как их реализовать и как они используются в стандартной библиотеке и других сторонних пакетах. В этом посте мы рассмотрим простой заводской шаблон и поймем, почему это имеет смысл в Python.

Вступление

Паттерны дизайна стали популярным предметом с момента проектирования: элементы многоразового объектно-ориентированного программного обеспечения (a.k.a GOF) были выпущены еще в 1994 году. Цели GOF состояли в том, чтобы показать методы, a.k.a, чтобы улучшить объектно-ориентированный дизайн. В общей сложности книга продемонстрировала 23 шаблона, классифицированные в 3 группах:

  • Творческий

  • Поведенческий

  • Структурный

Среди творческих моделей у нас есть фабричный метод. Согласно книге, цель этого шаблона – определить интерфейс для создания объекта. Затем классы Subs решит, какой класс будет создан. Есть также еще один вариант под названием Simple Factory. Этот шаблон создает экземпляр объекта, не разоблачая детали, стоящие за конструкцией. В этой статье мы посмотрим, как это сделать на Python идиоматическим способом.

Когда это полезно? Можем ли мы просто позвонить конструктору напрямую?

Этот шаблон полезен, когда вам нужно выполнить дополнительную настройку перед вызовом конструктора. В следующем разделе мы увидим несколько примеров о том, как они используются в стандартной библиотеке Python, а также в сторонних пакетах, таких как Панды Анкет

использование

В этой части мы посмотрим, как этот шаблон используется на практике и как вы можете реализовать его самостоятельно.

Стандартная библиотека Python

DateTime Модуль является одним из самых важных в стандартной библиотеке. Он определяет несколько классов, таких как дата , DateTime и ВРЕМЯ . Этот модуль широко использует простой заводской шаблон. Реальный пример – это дата класс. У него есть метод под названием FromTimestAmp это создает дата экземпляры дают временную метку.

In [3]: from datetime import date

In [4]: date.fromtimestamp(time.time())
Out[4]: datetime.date(2020, 11, 10)

Если мы посмотрим на реализацию, мы увидим, что она извлекает год, месяц и день из время экземпляр и вызов конструктора ( cls ). Это та настройка, которая абстрагирована от пользователя.

    @classmethod
    def fromtimestamp(cls, t):
        "Construct a date from a POSIX timestamp (like time.time())."
        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
        return cls(y, m, d)

Еще один замечательный пример – Fromisocalendar Метод, который выполняет обширную настройку. Вместо того, чтобы оставлять его пользователю, класс предоставляет функциональность «бесплатно», скрывая это от вас.

# https://github.com/python/cpython/blob/c304c9a7efa8751b5bc7526fa95cd5f30aac2b92/Lib/datetime.py#L860-L893
...
    @classmethod
    def fromisocalendar(cls, year, week, day):
        """Construct a date from the ISO year, week number and weekday.
        This is the inverse of the date.isocalendar() function"""
        # Year is bounded this way because 9999-12-31 is (9999, 52, 5)
        if not MINYEAR <= year <= MAXYEAR:
            raise ValueError(f"Year is out of range: {year}")

        if not 0 < week < 53:
            out_of_range = True

            if week == 53:
                # ISO years have 53 weeks in them on years starting with a
                # Thursday and leap years starting on a Wednesday
                first_weekday = _ymd2ord(year, 1, 1) % 7
                if (first_weekday == 4 or (first_weekday == 3 and
                                           _is_leap(year))):
                    out_of_range = False

            if out_of_range:
                raise ValueError(f"Invalid week: {week}")

        if not 0 < day < 8:
            raise ValueError(f"Invalid weekday: {day} (range is [1, 7])")

        # Now compute the offset from (Y, 1, 1) in days:
        day_offset = (week - 1) * 7 + (day - 1)

        # Calculate the ordinal day for monday, week 1
        day_1 = _isoweek1monday(year)
        ord_day = day_1 + day_offset

        return cls(*_ord2ymd(ord_day))
....

Панды

Панды является одним из наиболее используемых пакетов Python благодаря росту науки о данных и машинного обучения. Так же, как Python, Панды Также использует заводские методы. Классический пример – from_dict Метод, который принадлежит DataFrame класс.

        >>> data = {'row_1': [3, 2, 1, 0], 'row_2': ['a', 'b', 'c', 'd']}
        >>> pd.DataFrame.from_dict(data, orient='index')
               0  1  2  3
        row_1  3  2  1  0
        row_2  a  b  c  d

Когда мы осматриваем реализацию, мы также можем увидеть много настроек и дополнительных проверок.

    @classmethod
    def from_dict(cls, data, orient="columns", dtype=None, columns=None) -> DataFrame:
        ...
        index = None
        orient = orient.lower()
        if orient == "index":
            if len(data) > 0:
                # TODO speed up Series case
                if isinstance(list(data.values())[0], (Series, dict)):
                    data = _from_nested_dict(data)
                else:
                    data, index = list(data.values()), list(data.keys())
        elif orient == "columns":
            if columns is not None:
                raise ValueError("cannot use columns parameter with orient='columns'")
        else:  # pragma: no cover
            raise ValueError("only recognize index or columns for orient")

        return cls(data, index=index, columns=columns, dtype=dtype)

Как его реализовать

Самый идиоматический способ реализации фабричных методов в Python – это украшение их как Classmethod Анкет В Python регулярные методы прикрепляются к экземпляру объекта. Мы можем получить доступ к полям объектов через я аргумент Classmethod , с другой стороны, связаны не с экземпляром, а с класс Анкет Это означает, когда мы называем Myclass.factory_method Мы проходим Myclass Как первый аргумент, называемый CLS Анкет Это свойство делает их отличной альтернативой для заводских методов с момента вызова Cls (args) Внутри Classmethod так же, как Myclass (args) .

Для разработки ваших собственных фабричных методов достаточно, чтобы украсить его как Classmethod и вернуть новый экземпляр, построенный с CLS аргумент Например, предполагайте, что мы хотим реализовать Точка Класс, и мы хотим, чтобы он также был построен из полярных координат. Дополнительная установка для преобразования из полярного в картезиан хранится внутри метода. Не просто это более читаемо, но также упрощает конструктор.

class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    @classmethod
    def from_polar(cls, r: float, theta: float) -> "Point":
        """
        Converts a polar coordinate into cartesian point.

        >>> Point.from_polar(r=-2**0.5, theta=math.pi / 4)
        Point(x=-1.00, y=-1.00)
        """
        return cls(r * math.cos(theta), r * math.sin(theta))

    def __repr__(self):
        return f"{self.__class__.__name__}(x={self.x:.2f}, y={self.y:.2f})"

>>> Point.from_polar(r=-2**0.5, theta=math.pi / 4)
Point(x=-1.00, y=-1.00)

Вывод

Это в значительной степени! Я надеюсь, что вы узнали что -то другое и полезное. Простые заводские методы очень прохладные и могут абстрагировать много шаблонов. Не говоря уже о том, что он делает ваш код чистым и читаемым. В этом посте я показал, как этот шаблон используется в стандартной библиотеке и в других пакетах, таких как Панды Анкет

Другие сообщения, которые вам могут понравиться:

Этот пост был первоначально опубликован в https://miguendes.me/design-patterns-tasmake-sense-in-python-simple-factory

Увидимся в следующий раз!

Оригинал: “https://dev.to/miguendes/design-patterns-that-make-sense-in-python-simple-factory-2f8c”