Mengapa Anda perlu secara eksplisit memiliki argumen "diri" dalam metode Python?


197

Saat mendefinisikan metode pada kelas dengan Python, tampilannya seperti ini:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Tetapi dalam beberapa bahasa lain, seperti C #, Anda memiliki referensi ke objek yang terikat metode dengan kata kunci "ini" tanpa menyatakannya sebagai argumen dalam prototipe metode.

Apakah ini keputusan desain bahasa yang disengaja dalam Python atau ada beberapa detail implementasi yang memerlukan pengesahan "diri" sebagai argumen?


15
Saya yakin Anda juga akan tertarik mengetahui mengapa Anda perlu menulis secara eksplisit selfuntuk mengakses anggota - stackoverflow.com/questions/910020/…
Piotr Dobrogost

1
Tapi kelihatannya seperti pelat ketel
Raghuveer

Agak membingungkan tapi layak dipahami stackoverflow.com/a/31367197/1815624
CrandellWS

Jawaban:


91

Saya suka mengutip Peters 'Zen of Python. "Eksplisit lebih baik daripada implisit."

Di Java dan C ++, ' this.' dapat disimpulkan, kecuali bila Anda memiliki nama variabel yang membuatnya tidak mungkin untuk disimpulkan. Jadi, terkadang Anda membutuhkannya dan terkadang tidak.

Python memilih untuk membuat hal-hal seperti ini eksplisit daripada berdasarkan aturan.

Selain itu, karena tidak ada yang tersirat atau diasumsikan, bagian dari implementasi diekspos. self.__class__, self.__dict__dan struktur "internal" lainnya tersedia dengan cara yang jelas.


53
Meskipun akan lebih baik untuk memiliki pesan kesalahan yang kurang samar ketika Anda melupakannya.
Martin Beckett

9
Namun ketika Anda memanggil metode Anda tidak harus melewati variabel objek, bukankah itu melanggar aturan kejujuran? Jika untuk menjaga zen ini, harus berupa sesuatu seperti: object.method (objek, param1, param2). Sepertinya tidak konsisten ...
Vedmant

10
"eksplisit lebih baik daripada implisit" - Bukankah "gaya" Python dibangun di sekitar hal-hal yang tersirat? misalnya tipe data implisit, batas fungsi implisit (tidak ada {} 's), lingkup variabel implisit ... jika variabel global dalam modul tersedia dalam fungsi ... mengapa tidak logika / penalaran yang sama diterapkan ke kelas? Bukankah aturan yang disederhanakan hanya "apa pun yang dinyatakan pada tingkat yang lebih tinggi tersedia di tingkat yang lebih rendah" sebagaimana ditentukan oleh lekukan?
Simon

13
"Eksplisit lebih baik daripada implisit" Omong kosong terdeteksi
Vahid Amiri

10
hadapi saja, itu buruk. Tidak ada alasan untuk ini. Itu hanya peninggalan jelek tapi tidak apa-apa.
Toskan

62

Ini untuk meminimalkan perbedaan antara metode dan fungsi. Ini memungkinkan Anda untuk dengan mudah menghasilkan metode dalam metaclasses, atau menambahkan metode saat runtime ke kelas yang sudah ada.

misalnya

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Itu juga (sejauh yang saya tahu) membuat implementasi runtime python lebih mudah.


10
+1 untuk Ini untuk meminimalkan perbedaan antara metode dan fungsi. Ini harus diterima jawaban
pengguna

Ini juga merupakan akar dari penjelasan guido yang sangat terkait.
Marcin

1
Ini juga menunjukkan bahwa dalam Python, ketika Anda melakukan c.bar () pertama-tama memeriksa instance untuk atribut, kemudian memeriksa atribut kelas . Jadi Anda bisa 'melampirkan' data atau fungsi (objek) ke Kelas kapan saja dan berharap untuk mengakses dalam instance-nya (yaitu dir (instance) akan bagaimana caranya). Bukan hanya ketika Anda "menciptakan" c instance. Sangat dinamis.
Nishant

10
Saya tidak benar-benar membelinya. Bahkan dalam kasus di mana Anda membutuhkan kelas induk, Anda masih bisa menyimpulkannya saat eksekusi. Dan kesetaraan antara metode instance dan fungsi kelas yang melewati instance adalah konyol; Ruby baik-baik saja tanpa mereka.
zachaysan

2
JavaScript memungkinkan Anda untuk menambahkan metode ke objek saat run-time, dan itu tidak memerlukan selfdalam deklarasi fungsi (ingatlah, mungkin ini melempar batu dari rumah kaca, karena JavaScript memiliki beberapa thissemantik mengikat yang cukup rumit )
Jonathan Benn

55

Saya menyarankan agar seseorang membaca blog Guido van Rossum tentang topik ini - Mengapa diri eksplisit harus tetap ada .

Ketika definisi metode didekorasi, kita tidak tahu apakah akan secara otomatis memberikan parameter 'mandiri' atau tidak: dekorator dapat mengubah fungsi menjadi metode statis (yang tidak memiliki 'mandiri'), atau metode kelas (yang memiliki jenis lucu diri yang merujuk ke kelas bukan instance), atau bisa melakukan sesuatu yang sama sekali berbeda (itu sepele untuk menulis dekorator yang mengimplementasikan '@classmethod' atau '@staticmethod' dengan Python murni). Tidak mungkin tanpa mengetahui apa yang dilakukan dekorator apakah akan memberikan metode yang didefinisikan dengan argumen 'diri' implisit atau tidak.

Saya menolak retas seperti casing khusus '@classmethod' dan '@staticmethod'.


16

Python tidak memaksa Anda untuk menggunakan "diri". Anda dapat memberikan nama apa pun yang Anda inginkan. Anda hanya perlu mengingat bahwa argumen pertama dalam header definisi metode adalah referensi ke objek.


Namun, berdasarkan konvensi, ini harus 'mandiri' untuk instance atau 'cls' di mana jenis-jenisnya terlibat (mmmm metaclasses)
pobk

1
Itu memaksa untuk menempatkan diri sebagai param pertama dalam setiap metode, hanya teks tambahan yang tidak masuk akal bagi saya. Bahasa lain berfungsi dengan baik dengan ini.
Vedmant

Apakah saya benar ? selalu parameter pertama adalah referensi ke objek.
Mohammad Mahdi KouchakYazdi

@ MMKY Tidak, misalnya dengan @staticmethoditu tidak.
Markus

1
"Anda hanya perlu mengingat bahwa argumen pertama dalam definisi metode ..." Saya bereksperimen dengan mengubah kata "diri" menjadi "kwyjibo" dan itu masih berhasil. Jadi seperti yang sering dijelaskan, itu bukan kata "diri" yang penting tetapi posisi apa pun yang menempati ruang itu (?)
RBV

7

Juga memungkinkan Anda untuk melakukan ini: (singkatnya, memohon Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)akan mengembalikan 12, tetapi akan melakukannya dengan cara yang paling gila.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Tentu saja, ini lebih sulit untuk dibayangkan dalam bahasa seperti Java dan C #. Dengan membuat referensi diri eksplisit, Anda bebas untuk merujuk ke objek apa pun dengan referensi diri itu. Selain itu, cara bermain dengan kelas saat runtime lebih sulit dilakukan dalam bahasa yang lebih statis - bukan berarti itu baik atau buruk. Hanya saja diri yang eksplisit memungkinkan semua kegilaan ini ada.

Selain itu, bayangkan ini: Kami ingin menyesuaikan perilaku metode (untuk profil, atau sihir hitam gila). Ini dapat membuat kita berpikir: bagaimana jika kita memiliki kelas Methodyang perilakunya dapat kita atur atau kendalikan?

Nah ini dia:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

Dan sekarang: InnocentClass().magic_method()akan bertindak seperti yang diharapkan. Metode ini akan diikat dengan innocent_selfparameter ke InnocentClass, dan dengan magic_selfke instance MagicMethod. Aneh ya? Ini seperti memiliki 2 kata kunci this1danthis2 dalam bahasa seperti Java dan C #. Sihir seperti ini memungkinkan kerangka kerja untuk melakukan hal-hal yang seharusnya jauh lebih bertele-tele.

Sekali lagi, saya tidak ingin mengomentari etika dari hal ini. Saya hanya ingin menunjukkan hal-hal yang akan lebih sulit dilakukan tanpa referensi diri yang eksplisit.


4
Ketika saya mempertimbangkan contoh pertama Anda, saya dapat melakukan hal yang sama di Jawa: kelas dalam perlu dipanggil OuterClass.thisuntuk mendapatkan 'diri' dari kelas luar, tetapi Anda masih dapat menggunakan thissebagai referensi untuk dirinya sendiri; sangat mirip dengan apa yang Anda lakukan di sini di Python. Bagi saya tidak sulit membayangkan ini. Mungkin itu tergantung pada kemahiran seseorang dalam bahasa yang bersangkutan?
klaar

Tetapi bisakah Anda masih merujuk pada lingkup apa pun ketika Anda berada di dalam metode kelas anonim, yang didefinisikan di dalam kelas anonim, yang didefinisikan di dalam implementasi antarmuka anonim Something, yang pada gilirannya didefinisikan di dalam implementasi anonim lainnya Something? Dalam python tentu saja Anda bisa merujuk ke salah satu cakupan.
vlad-ardelean

Anda benar, di Java Anda hanya bisa merujuk ke kelas luar dengan memanggil classname eksplisit dan menggunakannya untuk awalan this. Referensi implisit tidak mungkin di Jawa.
klaar

Saya bertanya-tanya apakah ini akan berhasil: Dalam setiap ruang lingkup (setiap metode) memiliki variabel lokal, yang mereferensikan thishasilnya. Misalnya Object self1 = this;(baik menggunakan Object, atau sesuatu yang kurang generik). Kemudian, jika Anda memiliki akses ke variabel dalam lingkup yang lebih tinggi, Anda bisa memiliki akses ke self1, self2, ... selfn. Saya pikir ini harus dinyatakan final atau sesuatu, tetapi mungkin berhasil.
vlad-ardelean

3

Saya pikir alasan sebenarnya selain "The Zen of Python" adalah bahwa Functions adalah warga negara kelas satu di Python.

Yang pada dasarnya menjadikan mereka sebagai Obyek. Sekarang Masalah mendasarnya adalah jika fungsi Anda juga objek, dalam paradigma berorientasi objek bagaimana Anda mengirim pesan ke Objek ketika pesan itu sendiri adalah objek?

Tampak seperti masalah telur ayam, untuk mengurangi paradoks ini, satu-satunya cara yang mungkin adalah dengan melewatkan konteks eksekusi ke metode atau mendeteksinya. Tetapi karena python dapat memiliki fungsi bersarang, tidak mungkin melakukannya karena konteks eksekusi akan berubah untuk fungsi dalam.

Ini berarti satu-satunya solusi yang mungkin adalah secara eksplisit melewati 'diri' (Konteks eksekusi).

Jadi saya percaya ini adalah masalah implementasi Zen yang datang jauh kemudian.


Hai Saya baru ke python (dari latar belakang java) dan saya tidak cukup mengalir apa yang Anda katakan "bagaimana Anda mengirim pesan ke Objek ketika pesan itu sendiri adalah objek". Mengapa itu menjadi masalah, bisakah Anda menguraikan?
Qiulang

1
@ Qiulang Aah, dalam metode pemograman yang berorientasi objek, objek setara dengan mengirim pesan ke objek dengan atau tanpa payload (parameter untuk fungsi Anda). Metode secara internal akan direpresentasikan sebagai blok kode yang terkait dengan kelas / objek dan menggunakan lingkungan implisit yang tersedia untuknya melalui objek yang diminta. Tetapi jika metode Anda adalah objek, mereka bisa eksis tanpa dikaitkan dengan kelas / objek yang menimbulkan pertanyaan jika Anda memanggil metode ini lingkungan mana yang akan dihadapinya?
pankajdoharey

Dengan demikian harus ada mekanisme untuk menyediakan lingkungan, diri akan berarti lingkungan saat ini pada titik eksekusi tetapi Anda juga bisa menyediakan lingkungan lain.
pankajdoharey

2

Saya pikir itu ada hubungannya dengan PEP 227:

Nama dalam lingkup kelas tidak dapat diakses. Nama diselesaikan di lingkup fungsi terlampir terdalam. Jika definisi kelas terjadi dalam rantai cakupan bersarang, proses resolusi melewatkan definisi kelas. Aturan ini mencegah interaksi aneh antara atribut kelas dan akses variabel lokal. Jika operasi pengikatan nama terjadi dalam definisi kelas, itu menciptakan atribut pada objek kelas yang dihasilkan. Untuk mengakses variabel ini dalam suatu metode, atau dalam fungsi yang bersarang dalam suatu metode, referensi atribut harus digunakan, baik melalui diri sendiri atau melalui nama kelas.


1

Seperti yang dijelaskan dalam diri sendiri dengan Python, Demystified

apapun seperti obj.meth (args) menjadi Class.meth (obj, args). Proses panggilan otomatis sedangkan proses penerimaan tidak (eksplisit). Ini adalah alasan parameter pertama fungsi di kelas harus menjadi objek itu sendiri.

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

Doa:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () mendefinisikan tiga parameter tetapi kami baru saja melewati dua (6 dan 8). Demikian pula jarak () membutuhkan satu tetapi nol argumen disahkan.

Mengapa Python tidak mengeluh tentang ketidakcocokan nomor argumen ini ?

Secara umum, ketika kita memanggil metode dengan beberapa argumen, fungsi kelas yang sesuai dipanggil dengan menempatkan objek metode sebelum argumen pertama. Jadi, apapun seperti obj.meth (args) menjadi Class.meth (obj, args). Proses panggilan otomatis sedangkan proses penerimaan tidak (eksplisit).

Ini adalah alasan parameter pertama fungsi di kelas harus menjadi objek itu sendiri. Menulis parameter ini sebagai diri sendiri hanyalah sebuah konvensi . Ini bukan kata kunci dan tidak memiliki arti khusus dalam Python. Kami bisa menggunakan nama lain (seperti ini) tetapi saya sangat menyarankan Anda untuk tidak melakukannya. Menggunakan nama selain diri disukai oleh sebagian besar pengembang dan menurunkan keterbacaan kode ("Jumlah keterbacaan").
...
Dalam, contoh pertama self.x adalah atribut instance sedangkan x adalah variabel lokal. Mereka tidak sama dan terletak di ruang nama yang berbeda.

Diri Ada Di Sini Untuk Tetap

Banyak yang mengusulkan untuk menjadikan diri sebagai kata kunci dengan Python, seperti ini di C ++ dan Java. Ini akan menghilangkan penggunaan berlebihan diri eksplisit dari daftar parameter formal dalam metode. Walaupun ide ini tampak menjanjikan, itu tidak akan terjadi. Setidaknya tidak dalam waktu dekat. Alasan utamanya adalah kompatibilitas ke belakang . Berikut adalah blog dari pencipta Python sendiri yang menjelaskan mengapa diri eksplisit harus tetap ada.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.