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 «собачек», чем портить себе карму отзывами будущих поколений.
лучше написать «30 щенков»
Раз уж речь зашла о 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
Осталось ещё сравнить производительность всех вариантов. Выиграет наверняка только первый. Ибо всякие фокусы и манипуляции со строками, словаярями и метаинформацией затратны.
Only those users with full accounts are able to leave comments. Log in, please.