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

Эквивалент Builder в Python

В Java вы можете использовать шаблон конструктора, чтобы обеспечить более читаемое средство для создания экземпляра класса со многими параметрами. В шаблоне построителя создается объект конфигурации с методами для установки именованных атрибутов, а затем используется для построения другого объекта.

Что такое эквивалент в Python? Является ли лучший способ имитировать ту же реализацию?

4b9b3361

Ответ 1

Шаблоны проектирования часто могут быть заменены встроенными языковыми функциями.

Ваш прецедент

Вы говорите: "Я хотел иметь более читаемый" означает "создать экземпляр класса со многими параметрами". В случае Java:

[A] пример использования для шаблона построителя - это когда конструктор объекта, который должен быть построен, должен принимать очень много параметров. В таких случаях часто бывает удобно группировать такие параметры конфигурации в объекте-строителе (setMaxTemperature(int t), setMinTemperature(int t), set.. и т.д.), Чем обременять вызывающего абонента длинным списком аргументов, конструктор класса..

Не требуется шаблон Builder

Но Python поддерживает именованные параметры, поэтому это необязательно. Вы можете просто определить конструктор класса:

class SomeClass(object):
    def __init__(self, foo="default foo", bar="default bar", baz="default baz"):
        # do something

и назовите его с помощью именованных параметров:

s = SomeClass(bar=1, foo=0)

Обратите внимание, что вы можете свободно изменять порядок и опускать аргументы, так же как и с построителем в Java, вы можете опустить или переупорядочить вызовы методам set объекта-строителя.

Также стоит отметить, что динамическая природа Python дает вам больше свободы над построением объектов (с использованием __new__ и т.д.), что может заменить другие применения шаблона построителя.

Но если вы действительно хотите использовать его

вы можете использовать collections.namedtuple в качестве вашего объекта конфигурации. namedtuple() возвращает новый тип, представляющий кортеж, каждый из параметров которого имеет заданное имя, без необходимости писать класс шаблонов. Вы можете использовать объекты результирующего типа аналогично Java-сборщикам. (Спасибо Paul McGuire за это.)

StringBuilder

Связанный шаблон - это Java StringBuilder, который используется для эффективного построения (неизменяемого) String поэтапно. В Python это можно заменить на str.join. Например:

final StringBuilder sb = new StringBuilder();
for(int i = 0; i < 100; i++)
    sb.append("Hello(" + i + ")");
return sb.toString();

можно заменить на

return "".join("Hello({})".format(i) for i in range(100))

Ответ 2

OP настроен на падение, отбросив шаблон Builder как специфичный для Java. Не это. Это в Gang of Four book и потенциально относится к любому объектно-ориентированному языку.

К сожалению, даже статья статьи Википедии о шаблоне Builder не дает ему достаточно кредитов. Это не просто полезно для элегантности кода. Шаблоны Builder - отличный способ создать неизменяемые объекты, которые необходимо изменить до тех пор, пока они не будут использованы. Непрерывное состояние особенно важно в функциональных парадигмах, что делает Builder отличным объектно-ориентированным шаблоном для python.

Ниже приведен пример реализации Builder + ImmutableObject, используя collections.namedtuple, заимствованный и измененный с "Как создать неизменяемый объект в python". Я сохранил Builder довольно просто. Тем не менее, могут быть предусмотрены функции setter, которые возвращают Builder сам, чтобы разрешить цепочку вызовов. Или синтаксис @property можно использовать в Builder для предоставления адаптеров атрибутов, проверяющих достоверность атрибута до установки.

from collections import namedtuple

IMMUTABLE_OBJECT_FIELDS = ['required_function_result', 'required_parameter', 'default_parameter']

class ImmutableObjectBuilder(object):
    def __init__(self, required_function, required_parameter, default_parameter="foo"):
        self.required_function = required_function
        self.required_parameter = required_parameter
        self.default_parameter = default_parameter

    def build(self):
        return ImmutableObject(self.required_function(self.required_parameter),
                               self.required_parameter,
                               self.default_parameter)

class ImmutableObject(namedtuple('ImmutableObject', IMMUTABLE_OBJECT_FIELDS)):
    __slots__ = ()

    @property
    def foo_property(self):
        return self.required_function_result + self.required_parameter

    def foo_function(self):
        return self.required_function_result - self.required_parameter

    def __str__(self):
        return str(self.__dict__)

Пример использования:

my_builder = ImmutableObjectBuilder(lambda x: x+1, 2)
obj1 = my_builder.build()
my_builder.default_parameter = "bar"
my_builder.required_parameter = 1
obj2 = my_builder.build()
my_builder.required_function = lambda x: x-1
obj3 = my_builder.build()

print obj1
# prints "OrderedDict([('required_function_result', 3), ('required_parameter', 2), ('default_parameter', 'foo')])"
print obj1.required_function_result
# prints 3
print obj1.foo_property
# prints 5
print obj1.foo_function()
# prints 1
print obj2
# prints "OrderedDict([('required_function_result', 2), ('required_parameter', 1), ('default_parameter', 'bar')])"
print obj3
# prints "OrderedDict([('required_function_result', 0), ('required_parameter', 1), ('default_parameter', 'bar')])"

В этом примере я создал три объекта ImmutableObjects, все с разными параметрами. Я предоставил вызывающей программе возможность копировать, изменять и передавать измененную конфигурацию в форме строителя, сохраняя при этом неизменность встроенных объектов. Установка и удаление атрибутов в объектах ImmutableObject вызывает ошибки.

Итог: Builders - отличный способ передать что-то с изменчивым состоянием, которое предоставляет объект с неизменяемым состоянием, когда вы будете готовы его использовать. Или, по-разному, Builders - отличный способ предоставить установщики атрибутов, сохраняя при этом неизменное состояние. Это особенно ценно в функциональных парадигмах.

Ответ 3

Я не согласен с @MechanicalSnail. Я думаю, что реализация компилятора, аналогичная той, на которую ссылается плакат, по-прежнему очень полезна в некоторых случаях. Именованные параметры позволят вам просто установить переменные-члены. Если вы хотите сделать что-то немного сложнее, вам не повезло. В моем примере я использую шаблон классического построителя для создания массива.

class Row_Builder(object):
  def __init__(self):
    self.row = ['' for i in range(170)]

  def with_fy(self, fiscal_year):
    self.row[FISCAL_YEAR] = fiscal_year
    return self

  def with_id(self, batch_id):
    self.row[BATCH_ID] = batch_id
    return self

  def build(self):
    return self.row

Используя его:

row_FY13_888 = Row_Builder().with_fy('FY13').with_id('888').build()

Ответ 4

Шаблон построителя в Java может быть легко реализован в python с помощью варианта:

MyClass(self, required=True, someNumber=<default>, *args, **kwargs)

где required и someNumber - пример, показывающий требуемые параметры со значением по умолчанию, а затем чтение для переменных аргументов при обработке случая, когда может быть None

Если вы ранее не использовали переменные аргументы, обратитесь к this