Мета "повернення себе" з класового методу?


46

Я натрапив на щось подібне у проекті з відкритим кодом. Методи, що змінюють атрибути екземпляра, повертають посилання на екземпляр. Яке призначення цієї конструкції?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self

2
І ось так написано майже весь jQuery. Майже кожна функція повертає об’єкт jQuery
CaffGeek

11
Чому б вам не довірити написаний таким кодом?
sepp2k

Відповіді:


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- тобто об'єкт з оновленим станом є видом повернутого значення.

Отже, в мові ОО з змінним станом ці дві більш-менш еквівалентні:

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

... на відміну від

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

Тому я чув, що люди в минулому чинили опір вільним інтерфейсам, тому що їм подобається перша форма. Інша назва, яку я чув для "вільного інтерфейсу" - "аварія поїзда";)

Я кажу, що "більш-менш еквівалентний", тому що плавні інтерфейси додають зморшок. Вони не повинні "повертати це". Вони можуть "повернути нові". Це спосіб досягнення незмінних об'єктів в ОО.

Отже, у вас може бути клас A, який це робить (псевдокод)

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) можуть навіть виявити, коли один з цих нових об’єктів не використовується і просто не виділяє нічого.
8bittree

@ 8bittree: ми говоримо про Python, це питання було позначено Python 8 років тому. Python GC не чудовий, тому найкраще не витікати пам'ять в першу чергу. __init__Багаторазові дзвінки також вносять накладні витрати.
smci

8

Більшість мов знають ідіому "повернення", і ігнорують її, якщо вона не використовується в рядку. Однак помітно, що в Python функції повертаються Noneза замовчуванням.

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

Досить сказати, повернення self - це остаточний спосіб OO для створення класових методів, але Python дозволяє отримати кілька повернених значень, кортежів, списків, об'єктів, примітивів або None.

Як вони заявляють, ланцюжок - це просто перенесення відповіді на останню операцію в наступну, і час виконання Python може оптимізувати таку річ. Означення списку є вбудованою формою цього. (Дуже потужно!)

Тож у Python не так важливо, щоб кожен метод чи функція повертала речі, тому за замовчуванням немає None.

Існує школа думки, що кожна дія в програмі повинна повідомляти про її успіх, провал або результат у контексті або об'єкті, що викликає, але тоді не говорили про вимоги DOD ADA. Якщо вам потрібно отримати зворотний зв’язок від методу, продовжуйте, чи ні, але намагайтеся бути послідовними щодо цього.

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

Одне застереження полягає в тому, що якщо ви використовуєте ідіому, що повертається, Python дозволить призначити всі ваші методи змінним, і ви можете подумати, що ви отримуєте результат даних або список, коли ви фактично отримуєте об'єкт.

Мови, що обмежують тип, кричать, кричать і ламаються, коли ви намагаєтесь це зробити, але інтерпретовані (Python, Lua, Lisp) набагато динамічніші.


4

У Smalltalk кожен метод, який явно щось не повертає, має неявне "повернення".

Це тому, що (а) наявність кожного методу повернення чогось робить обчислювальну модель більш рівномірною, і (б) дуже корисно мати методи повернення самості. Джош К дає приклад.


Цікаво ... в тому Python, що кожен метод, який явно щось не повертає, має неявне "return None".
Робота

2

Плюси повернення об'єкта ( return self)

  • Приклад:

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

Плюси мутації об'єкта (ні return self)

  • Можливість використання returnв інших цілях.
  • Гвідо ван Россум схвалює цей стиль (я не згоден з його аргументом).
  • Більш популярний стиль у спільноті Python (я думаю).
  • За замовчуванням (менше вихідного коду).

Міркування Гвідо мене переконливе. Зокрема, частина, в якій він розповідає про те, "читач повинен бути глибоко знайомий з кожним із методів", тобто. Це змушує вас визначити, чи це сам ланцюжок або вихідний ланцюг або комбо, перш ніж зрозуміти, що у вас є в кінці ланцюг
Nath

@Nat Наскільки операції обробки рядків принципово відрізняються від решти операцій?
xged

Різниця полягає в тому, що строковий рядок, який ви створюєте, щоразу створює новий абсолютно інший об'єкт Тобто Не змінюючи елемент на місці. зрозуміло, що існуючий об'єкт не змінюється. У мові, де повернення "я" явне, навпаки - це навпаки, але змішане оточення видається проблематичним
Nath

@Matt Я просто згадав, що струни Python незмінні. Це повністю відповідає моєму питанню.
xged

1
@Matt "зрозуміло, що існуючий об'єкт не модифікується" - це не зрозуміло лише з вигляду.
xged
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.