Pull to refresh

Comments 34

Не читал статью, тк не интересен питон, но картинка весьма оригинально подобрана =)
А вот если я не читал ваш комментарий, судя по реакции сообщества стоит сразу минусануть? Не обижайтесь, я пошучиваю.
Очевидный недостаток — применимо только для методов, которые либо всегда возвращают None, либо всегда возвращают объект.

Метод типа такого оборачивать небезопасно:
def getChild(name):
return self.internalDict.get(name)

(да, можно спорить о том, что вместо None нужно вернуть null-object, но… все Non'ы замучаешься оборачивать)

Ну и в целом (имхо) недостаток метода в том, что он размывает границы между mutable и immutable объектами: читая код, неочевидно, вызываются ли все add,mul на одном объекте либо на цепочке созданных объектов.

Для похожего можно еще varargs использовать, здесь пример:

stackoverflow.com/questions/3883907/designing-an-python-api-fluent-interface-or-arguments

Вместо
vis = new pv.Panel().width(150).height(150);

использовать
vis = pv.Panel(width=150, height=150)
Для процессинга картинок и подобных операций лично мне понравилась идея с Pipe-ами. Этот подход позволяет не трогать реализацию самих классов.

И с getParent/getChild() вы верно заметили. Я бы лучше использовал второй вариант с декоратором: по крайней мере в моём коде потом не запутаются…
Спасибо за ссылку! Хех, трюк с __ror__ я раньше использовал (правда у меня был __rmod__), и честно думал что придумал что-то оригинальное :-)
varargs не подходит, если важен порядок операций:
$('#foo').slideUp(300).delay(800).fadeIn(400).delay(800);
if fn and type(fn)==MethodType стоило бы заменить на isinstance(fn, MethodType).

А по делу — хорошая демонстрация развития идеи и возможностей Пайтона :)
Вы бы рассказали, что это за интерфейсы такие, текучие. Не все читали статью про PHP.
Эта вечь больше известна под названием «чейнинг методов» (построение цепочки). Подход популяризован jQuery.
Метод с дек@ратором гораздо лучше, потому что explicit is better than implicit. Имхо.
Тоже считаю, что лучше уж написать 10 «собачек», чем портить себе карму отзывами будущих поколений.
Раз уж речь зашла о Python-way, то не лучше ли будет просто дописать «return self» в конце каждой функции? Цена тому — все таже одна строка кода, зато простоту понимания сторонним читателем повысит неимоверно, нежели декоратор.
Лучше конечно, но не так «илитарно» :)
Вставьте ссылку на статью про текучие интерфейсы на PHP в первое предложение поста, пожалуйста!
Ссылки указал. И, как уже упомянули выше, описать подход в двух словах можно: «как в jQuery».
Ссылку на статью стоит поставить в первое предложение. Собственно там, где вы её упоминаете.
Спасибо за статью! Добавьте, пожалуйста, пример реализации в википедию (текучие интерфейсы), а то там примеры на java, cpp, etc. есть, а на python нет.
аццкий сотона. все конечно весело и красиво, но не дай бог потом поддерживать и фиксить такой код в проекта с овер 200К строк кода, когда измениться апи, конструкции и версии
Тут же не говорится о том, что это нужно использовать везде и всегда. Такой подход можно применять для отдельных задач, частей проекта. Вообще познавательно. Спасибо автору.
способ 3 можно чуть модифицировать — если item.startswith('fl') оборачивать метод без 'fl' в fluent обертку, тогда при необходимости можно любой метод вызвать как текучий с сохранением доступа к основному «по умолчанию».

Хотим — пишем как:

instance.add(5)
instance.mul(3)


а хотим,

instance.fladd(5).flmul(3)
Очевидный недостаток — мы не можем использовать __getattribute__ в своих целях.
Не понял, разве с помощью super() это не решается?
Тогда надо модифицировать рабочий класс. Почему бы в таком случае не вписать всю обработку в него железно?
Можно на Ruby так подмешать, например… хотя смысла мало.

module Chaining
  def included(base)
    base.class_eval do
      instance_methods.each do |method|
        alias "#{method}_without_chain" method
        define_method method do |*args, &block|
          r = block_given? ? send("#{method}_without_chain", *args, &block) : send("#{method}_without_chain", *args)
          return self if r.nil?
          r
        end
      end
    end
  end
end

ThingBase.class_eval { include Chaining }
Третий способ от второго чем отличается-то? Ничем. Правильно как? Правильно искать методы в dict и замещать их на chained версии.

И почему вы каждый раз оборачиваете метод? Скорость работы себе представляете?
Правильно искать методы в dict и замещать их на chained версии

Соглашусь, не приходило в голову. Тогда надо ещё перехватывать __setattr__ для того чтобы можно было обернуть созданные или изменённые во время работы методы.
плюс в карму за первое предложение! :) ну и за статью отдельное спасибо!
UFO landed and left these words here
на вкус и цвет фломастеры разные, а вообще тут дело семантики. я на пхп делал мини-валидатор с FI по типу
$validator->validate($data,$rule)->validate($data,$rule)->isValid(); (проверка на корректность всех данных). Имхо очень удобно
как то так можно:

dev = (
    Device.init(port=Configs.dispenser_port, baudrate=Configs.dispenser_baudrate)
    .test(donors=[0,1], destination=[-1])
    .dispense(amount=100, donors=[0])
    .disconnect())


Например, я в SQLAlchemy запросы пишу таким образом

users_jets = (
     s.query(User, UserJet)
     .join((UserJet, User.current_jet_id == UserJet.id))
     .filter(User.id.in_(players_ids))
     .all())
UFO landed and left these words here
>Не очевидно, что метод test может принадлежать не классу Device (например, Device.init может возвращать объект Dispenser). А выглядит это так, будто Device.test существует.

определяющим фактором для fluent интерфейсов является сохранение контекста между вызовами методов (т.е. то, что метод возвращает self, а не что-то другое). поэтому en.wikipedia.org/wiki/Law_of_Demeter не нарушается и ни какой путаницы быть не может.

по большому счёту, смысл всей этой затеи состоит в том, чтобы упростить выражение, «вынеся за скобки» повторяющееся имя переменной. такое повторение часто получается при конфигурировании объектов.

существуют примеры реализации данного паттерна на уровне языка. например в VisualBasic, где программисты проводят массу времени, занимаясь конфигрурированием объектов:

With testObject
    .Height = 100
    .Text = "Hello, World"
    .ForeColor = System.Drawing.Color.Green
    .Font = New System.Drawing.Font(.Font, System.Drawing.FontStyle.Bold)
End With
Осталось ещё сравнить производительность всех вариантов. Выиграет наверняка только первый. Ибо всякие фокусы и манипуляции со строками, словаярями и метаинформацией затратны.
Sign up to leave a comment.

Articles