it-swarm-id.com

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

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?

180
Readonly

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

Dalam Java dan C++, 'this.' dapat disimpulkan, kecuali ketika 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 akan diekspos. self.__class__, self.__dict__ dan struktur "internal" lainnya tersedia dengan cara yang jelas.

88
S.Lott

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.

55
Ryan

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

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 retasan seperti casing khusus '@classmethod' dan '@staticmethod'.

51
bhadra

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.

16
Victor Noagbodji

Juga memungkinkan Anda untuk melakukan ini: (singkatnya, memanggil 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). Hal ini dapat membuat kita berpikir: bagaimana jika kita memiliki kelas Method yang 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 parameter innocent_self ke InnocentClass, dan dengan magic_self ke instance MagicMethod. Aneh ya? Ini seperti memiliki 2 kata kunci this1 dan this2 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 tentang hal ini. Saya hanya ingin menunjukkan hal-hal yang akan lebih sulit dilakukan tanpa referensi diri yang eksplisit.

6
vlad-ardelean

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 mendasar adalah jika fungsi Anda adalah objek juga, dalam paradigma berorientasi objek bagaimana Anda mengirim pesan ke Objek ketika pesan itu sendiri adalah objek? 

Sepertinya 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.

2
pankajdoharey

Saya pikir itu ada hubungannya dengan PEP 227:

Nama dalam lingkup kelas tidak dapat diakses. Nama diselesaikan di ruang lingkup fungsi melampirkan terdalam. Jika definisi kelas terjadi dalam rantai cakupan bersarang, proses resolusi melompati kelas definisi. Aturan ini mencegah interaksi aneh antara kelas atribut dan akses variabel lokal. Jika operasi mengikat nama terjadi dalam definisi kelas, itu menciptakan atribut pada .__ yang dihasilkan. objek kelas. Untuk mengakses variabel ini dalam suatu metode, atau dalam suatu fungsi bersarang dalam suatu metode, referensi atribut harus digunakan, baik via self atau via nama kelas.

2
daole

Sebagaimana dijelaskan dalam diri 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 nomor argumen ini tidak cocok?

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).

Inilah alasan mengapa parameter pertama dari suatu 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") . 
...
Di, contoh pertama self.x adalah atribut instance sedangkan x adalah variabel lokal. Mereka tidak sama dan terletak di ruang nama yang berbeda.

Cukup Ada Di Sini Untuk Tetap

Banyak yang telah 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. Meskipun ide ini tampak menjanjikan, itu tidak akan terjadi. Setidaknya tidak dalam waktu dekat. Alasan utama adalah kompatibilitas ke belakang. Berikut adalah blog dari pencipta Python sendiri yang menjelaskan mengapa diri eksplisit harus tetap ada.

0
mon