Как «одурачить» утку, набрав в Python - PullRequest
0 голосов
/ 05 ноября 2018

Предположим, у меня был класс А:

 class A:

     def __init__(self, x, y):
         self.x = x
         self.y = y

     def sum(self):
         return self.x + self.y

И я определил фабричный метод с именем factory:

def factory(x, y):

    class B: pass

    b = B()
    setattr(b, 'x', x)
    setattr(b, 'y', y)
    B.__name__ = 'A'
    return b

Теперь, если я сделаю print(type(A(1, 2))) и print(type(factory(1, 2))), они покажут, что это разные типы. И если я попытаюсь сделать factory(1, 2).sum(), я получу исключение. Но type(A).__name__ и type(factory(1, 2)).__name__ эквивалентны, и если я сделаю A.sum(factory(1, 2)), я получу 3, как если бы я звонил, используя A. Итак, мой вопрос таков:

Что мне нужно сделать, чтобы factory(1, 2).sum() работал без определения sum для B или наследования?

1 Ответ

0 голосов
/ 05 ноября 2018

Я думаю, что вы в корне неправильно понимаете шаблон фабрики и, возможно, путаетесь с тем, как работают интерфейсы в Python. Либо это, либо I в корне смущен этим вопросом. В любом случае, есть какая-то сортировка, которую мы должны сделать.

Что мне нужно сделать, чтобы фабрика (1, 2) .sum () работала без определение суммы на Б или занятие наследством?

Просто верните A вместо некоторого другого типа:

def factory(x, y):
    return A(x, y)

тогда

print(factory(1,2).sum())

выдаст 3, как и ожидалось. Но это своего рода бесполезная фабрика ... может просто сделать A(x, y) и покончить с этим!

Некоторые заметки:

  1. Обычно вы используете «фабричный» (или фабричный шаблон), когда у вас есть легко «именуемые» типы, которые могут быть нетривиальными для построения. Подумайте, как, когда вы используете scipy.interpolate.interp1d (см. здесь ), есть опция для kind, которая в основном является перечислением для всех различных стратегий, которые вы можете использовать для интерполяции. По сути, это фабрика (но скрытая внутри функции для простоты использования). Вы можете представить, что это может быть автономно, поэтому вы называете свою фабрику «стратегии», а затем передаете это на interp1d вызов. Тем не менее, делать это встроенным является распространенной моделью в Python. Заметьте: эти стратегии легко «назвать», их довольно сложно построить в целом (вы можете себе представить, что было бы неприятно передавать функцию, которая выполняет линейную интерполяцию, а не просто kind='linear'). Вот что делает фабричный образец полезным ...

  2. Если вы не знаете, что такое A, то это определенно не тот шаблон, который вы хотели бы применить. Кроме того, если вы не знаете, что сериализуете / десериализуете, было бы невозможно назвать это или использовать это. Вы должны знать это или иметь какой-то способ сделать вывод.

  3. Интерфейсы в Python не принудительно применяются, как и в других языках, таких как Java / C ++. В этом дух утки. Если интерфейс выполняет что-то вроде вызова x.sum(), тогда не имеет значения, какой тип x на самом деле, он просто должен иметь метод с именем sum(). Если он действует как утка «сумма», крякает как утка «сумма», то он является уткой «сумма» с точки зрения Python. Не имеет значения, является ли x массивом numpy или A, все равно будет работать. В Java / C ++ подобные вещи не будут компилироваться , если только компилятор не будет абсолютно уверен , что x имеет определенный метод sum. К счастью, Python не такой, поэтому вы можете определить его на лету (что, возможно, вы пытались сделать с B). В любом случае, интерфейсы в Python сильно отличаются от других основных языков.

P.S.

Но, type(A).__name__ и type(factory(1, 2)).__name__ эквивалентны

Из Конечно Они есть, вы явно делаете это, когда говорите B.__name__ = 'A'. Так что я не уверен, что вы пытались получить там ...

НТН!

...