Tujuan `kembali sendiri` dari metode kelas?

46

Saya menemukan sesuatu seperti ini di proyek sumber terbuka. Metode yang mengubah atribut instance mengembalikan referensi ke instance. Apa tujuan konstruk ini?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self
nate c
sumber
2
Dan ini adalah bagaimana hampir semua jQuery ditulis. Hampir setiap fungsi mengembalikan objek jQuery
CaffGeek
11
Mengapa Anda tidak mempercayai kode yang ditulis seperti itu?
sepp2k

Jawaban:

65

Ini untuk memungkinkan rantai.

Sebagai contoh:

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;
        },
    };
}

Sekarang Anda bisa mengatakan:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;
Josh K.
sumber
20
Sebagai catatan, rangkaian ini umumnya terlihat pada kode dengan antarmuka yang lancar .
Lie Ryan
10

Seperti yang disebutkan oleh @Lie Ryan dan @Frank Shearar, itu disebut "antarmuka yang lancar" tetapi pola itu sudah ada sejak lama.

Bagian yang kontroversial dari pola itu adalah bahwa dalam OO, Anda memiliki status yang bisa berubah, sehingga metode batal memiliki jenis nilai pengembalian tersirat this- yaitu, objek dengan status yang diperbarui adalah jenis nilai pengembalian.

Jadi, dalam bahasa OO dengan status dapat berubah, keduanya lebih kurang sama:

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

...sebagai lawan

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

Jadi saya pernah mendengar orang di masa lalu menolak antarmuka yang lancar karena mereka menyukai bentuk pertama. Nama lain yang pernah saya dengar untuk "antarmuka yang lancar" adalah "train wreck";)

Saya mengatakan "kurang lebih setara", karena antarmuka yang lancar menambah kerutan. Mereka tidak harus "mengembalikan ini". Mereka dapat "mengembalikan yang baru". Itu cara untuk mencapai objek yang tidak dapat diubah di OO.

Jadi Anda bisa memiliki kelas A yang melakukan (pseudocode)

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

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

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

Sekarang, setiap metode mengembalikan objek baru, meninggalkan objek awal tidak berubah. Dan itu adalah cara masuk ke objek yang tidak dapat diubah tanpa benar-benar banyak berubah dalam kode Anda.

rampok
sumber
Ok tetapi jika metode Anda kembali new(...), itu akan membocorkan memori.
smci
@smci Itu akan sangat bergantung pada bahasa, implementasi, dan penggunaan. Tentu, jika itu C ++, kemungkinan Anda akan membocorkan memori di semua tempat. Tapi ini semua sepele akan dibersihkan oleh bahasa dengan pengumpul sampah. Beberapa implementasi (dengan atau tanpa GC) bahkan dapat mendeteksi ketika salah satu dari objek-objek baru tersebut tidak digunakan dan tidak benar-benar mengalokasikan apa pun.
8bittree
@ 8bittree: kita berbicara tentang Python, pertanyaan ini ditandai Python 8 tahun yang lalu. Python GC tidak bagus jadi sebaiknya jangan bocor memori sejak awal. Panggilan __init__berulang kali juga memperkenalkan overhead.
smci
8

Sebagian besar bahasa menyadari idiom 'kembali sendiri', dan abaikan saja jika tidak digunakan dalam satu baris. Namun perlu dicatat bahwa dalam Python, fungsi kembali Nonesecara default.

Ketika saya di sekolah CS instruktur saya membuat besar kesepakatan tentang perbedaan antara fungsi, prosedur, rutinitas, dan metode; banyak pertanyaan esai kram tangan diselesaikan dengan pensil mekanik yang menjadi panas di tangan saya tentang semua itu.

Cukuplah untuk mengatakan, kembali diri adalah cara OO definitif untuk membuat metode kelas, tetapi Python memungkinkan untuk beberapa nilai kembali, tupel, daftar, objek, primitif, atau Tidak ada.

Chaining, seperti yang mereka katakan, hanyalah menempatkan jawaban untuk operasi terakhir ke yang berikutnya, dan runtime dari Python dapat mengoptimalkan hal semacam itu. Pemahaman daftar adalah bentuk bawaan dari hal ini. (Sangat kuat!)

Jadi dalam Python tidak begitu penting bahwa setiap metode atau fungsi mengembalikan sesuatu, itulah sebabnya defaultnya adalah None.

Ada aliran pemikiran bahwa setiap tindakan dalam suatu program harus melaporkan keberhasilan, kegagalan, atau hasilnya kembali ke konteks atau objek yang memohon, tetapi kemudian tidak berbicara Persyaratan DOD ADA di sini. Jika Anda perlu mendapatkan umpan balik dari suatu metode, silakan, atau tidak, tetapi cobalah untuk konsisten tentang hal itu.

Jika suatu metode dapat gagal, itu harus mengembalikan keberhasilan atau kegagalan atau menimbulkan pengecualian untuk ditangani.

Satu peringatan adalah bahwa jika Anda menggunakan idiom self return, Python akan memungkinkan Anda untuk menetapkan semua metode Anda ke variabel dan Anda mungkin berpikir Anda mendapatkan hasil data atau daftar ketika Anda benar-benar mendapatkan objek.

Jenis-bahasa yang membatasi berteriak dan berteriak ketika Anda mencoba melakukan ini, tetapi yang ditafsirkan (Python, Lua, Lisp) jauh lebih dinamis.

Chris Reid
sumber
4

Dalam Smalltalk, setiap metode yang tidak secara eksplisit mengembalikan sesuatu, memiliki "return self" yang tersirat.

Itu karena (a) setiap metode mengembalikan sesuatu membuat model komputasi lebih seragam dan (b) sangat berguna memiliki metode mengembalikan sendiri. Josh K memberikan contoh yang bagus.

Frank Shearar
sumber
Menarik ... dalam Python, setiap metode yang tidak secara eksplisit mengembalikan sesuatu, memiliki "return None" yang tersirat.
Ayub
2

Kelebihan mengembalikan objek ( return self)

  • Contoh:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • Gaya yang lebih populer di komunitas pemrograman (saya pikir).

Kelebihan bermutasi objek (tidak ada return self)

  • Kemampuan untuk digunakan returnuntuk tujuan lain.
  • Guido van Rossum menyetujui gaya ini (saya tidak setuju dengan argumennya).
  • Gaya yang lebih populer di komunitas Python (saya pikir).
  • Default (kurangi kode sumber).
xged
sumber
Argumen Guido menarik bagi saya. Khususnya bagian di mana ia berbicara tentang "pembaca harus akrab dengan masing-masing metode" Yaitu memaksa Anda perlu menentukan apakah ini rantai diri atau dan rantai output atau kombo sebelum Anda memahami apa yang Anda miliki di akhir rantai
Nath
@Nat Bagaimana operasi pemrosesan string secara fundamental berbeda dari operasi lainnya?
xged
Perbedaannya adalah bahwa case string Anda menghasilkan objek yang sama sekali berbeda setiap kali. Yaitu Tidak memodifikasi item di tempat. sudah jelas bahwa objek yang ada tidak sedang dimodifikasi. Dalam bahasa di mana pengembalian diri tersirat, yang sebaliknya adalah benar tetapi lingkungan campuran tampaknya bermasalah
Nath
@Matt Saya baru ingat bahwa string Python tidak dapat diubah. Itu sepenuhnya menjawab pertanyaan saya.
Ditandai
1
@ Matt "sudah jelas bahwa objek yang ada tidak sedang dimodifikasi" - tidak jelas hanya dari melihat sekalipun.
xged