Цель `вернуть себя` из метода класса?

46

Я столкнулся с чем-то вроде этого в проекте с открытым исходным кодом. Методы, которые изменяют атрибуты экземпляра, возвращают ссылку на экземпляр. Какова цель этой конструкции?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self
Нейт С
источник
2
И это то, как написано в значительной степени все JQuery. Почти каждая функция возвращает объект jQuery
CaffGeek
11
Почему вы не доверяете написанному коду?
2010 г.

Ответы:

65

Это разрешить цепочку.

Например:

var Car = function () {
    return {
        gas : 0,
        miles : 0,
        drive : function (d) {
            this.miles += d;
            this.gas -= d;
            return this;
        },
        fill : function (g) {
            this.gas += g;
            return this;
        },
    };
}

Теперь вы можете сказать:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;
Джош К
источник
20
для записи, этот вид цепочки обычно встречается в коде с плавным интерфейсом .
Ли Райан
10

Как упоминают @Lie Ryan и @Frank Shearar, это называется «плавным интерфейсом», но этот паттерн существует очень давно.

Спорная часть этого шаблона состоит в том, что в OO у вас есть изменяемое состояние, поэтому у метода void есть некое подразумеваемое возвращаемое значение, thisто есть объект с обновленным состоянием является своего рода возвращаемым значением.

Таким образом, в языке OO с изменяемым состоянием эти два более или менее эквивалентны:

a.doA()
a.doB()
a.doC()

... в отличие от

a.doA().doB().doC()

Так что я слышал, что люди в прошлом сопротивлялись беглым интерфейсам, потому что им нравится первая форма. Другое название, которое я слышал для «свободного интерфейса» - «крушение поезда»;)

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

Таким образом, вы могли бы иметь класс А, который делает (псевдокод)

function doA():
    return new A(value + 1)

function doB():
    return new A(value * 2)

function doC():
    return new A(sqrt(value))

Теперь каждый метод возвращает новый объект, оставляя исходный объект без изменений. И это способ попасть в неизменяемые объекты без особых изменений в вашем коде.

обкрадывать
источник
Хорошо, но если ваши методы вернутся new(...), это приведет к утечке памяти.
smci
@smci Это сильно зависит от языка, реализации и использования. Конечно, если это C ++, вы, вероятно, повсеместно будете терять память. Но все это будет тривиально очищено языком со сборщиком мусора. Некоторые реализации (с GC или без GC) могут даже обнаруживать, когда один из этих новых объектов не используется, и просто ничего не выделять.
8bittree
@ 8bittree: мы говорим о Python, этот вопрос был помечен как Python 8 лет назад. Python GC не очень хорош, так что лучше не пропускать память. Вызов __init__многократно вводит накладные расходы тоже.
июня
8

Большинство языков знают идиому «возврата себя» и игнорируют его, если он не используется в строке. Однако следует отметить, что в Python функции возвращаются Noneпо умолчанию.

Когда я был в школе CS мой инструктор сделал огромное дело о разнице между функциями, процедурами, процедуры и методы; многие вопросы о сочинении рук были заточены механическими карандашами в моих руках от всего этого.

Достаточно сказать, что возвращение self является окончательным ОО-способом для создания методов класса, но Python допускает множественные возвращаемые значения, кортежи, списки, объекты, примитивы или None.

Цепочка, как они выражаются, просто помещает ответ на последнюю операцию в следующую, и среда выполнения Python может оптимизировать подобные вещи. Понимания списка являются встроенной формой этого. (Очень могущественный!)

Так что в Python не так важно, чтобы каждый метод или функция возвращали что-то, поэтому по умолчанию None.

Существует точка зрения, что каждое действие в программе должно сообщать об успехе, неудаче или результате обратно в вызывающий контекст или объект, но тогда здесь не говорилось о требованиях DOD ADA. Если вам нужно получить обратную связь от метода, продолжайте или нет, но постарайтесь быть последовательным в этом.

Если метод может потерпеть неудачу, он должен вернуть успех или неудачу или вызвать исключение для обработки.

Одно предостережение: если вы используете возвращаемую идиому self, Python позволит вам назначить все ваши методы переменным, и вы можете подумать, что получаете результат данных или список, когда фактически получаете объект.

Языки с ограничением типов кричат, кричат ​​и ломаются, когда вы пытаетесь это сделать, но интерпретируемые (Python, Lua, Lisp) гораздо более динамичны.

Крис Рид
источник
4

В Smalltalk каждый метод, который явно не возвращает что-либо, имеет неявное «return self».

Это потому, что (а) наличие у каждого метода возврата чего-либо делает вычислительную модель более однородной, и (б) очень полезно иметь методы, возвращающие себя. Джош К дает хороший пример.

Фрэнк Шиарар
источник
Интересно ... Pythonкаждый метод, который явно не возвращает что-то, имеет неявное "return None".
Работа
2

Плюсы возврата объекта ( return self)

  • Пример:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • Более популярный стиль в сообществе программистов (я думаю).

Плюсы мутирования объекта (нет return self)

  • Возможность использования returnдля других целей.
  • Гидо ван Россум одобряет этот стиль (я не согласен с его аргументом).
  • Более популярный стиль в сообществе Python (я думаю).
  • По умолчанию (меньше исходного кода).
xged
источник
Аргументация Гвидо убедительна для меня. В частности, в той части, где он говорит о том, что «читатель должен быть близко знаком с каждым из методов», т. Е. Это заставляет вас определить, является ли это цепочкой самообучения или цепочкой вывода или комбинацией, прежде чем вы поймете, что у вас есть в конце цепь
Нат
@Nat Чем операции обработки строк принципиально отличаются от остальных операций?
xged
Разница в том, что в случае строки вы каждый раз генерируете совершенно новый объект. Т.е. не модифицировать предмет на месте. Понятно, что существующий объект не изменяется. В языке, где возвращение «я» подразумевается, верно и обратное, но смешанная среда кажется проблематичной
Нат
@Matt Я только что вспомнил, что строки Python неизменны. Это полностью отвечает на мой вопрос.
xged
1
@Matt "ясно, что существующий объект не изменяется" - это не ясно, хотя смотреть.
xged