diri di Python, Demystified

Sekiranya anda telah memprogramkan di Python (pengaturcaraan berorientasi objek) untuk beberapa waktu, maka anda pasti menemui kaedah yang mempunyai selfparameter pertama.

Mari kita cuba memahami apa itu parameter diri berulang ini.

Apa itu diri di Python?

Dalam pengaturcaraan berorientasikan objek, setiap kali kita menentukan kaedah untuk kelas, kita gunakan selfsebagai parameter pertama dalam setiap kes. Mari lihat definisi kelas yang dipanggil Cat.

 class Cat: def __init__(self, name, age): self.name = name self.age = age def info(self): print(f"I am a cat. My name is (self.name). I am (self.age) years old.") def make_sound(self): print("Meow")

Dalam kes ini semua metode, termasuk __init__, memiliki parameter pertama sebagai self.

Kami tahu bahawa kelas adalah cetak biru untuk objek. Cetakan biru ini dapat digunakan untuk membuat banyak objek. Mari buat dua objek berbeza dari kelas di atas.

 cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)

Kata selfkunci digunakan untuk mewakili contoh (objek) kelas yang diberikan. Dalam kes ini, kedua-dua Catobjek cat1dan cat2mempunyai mereka sendiri namedan agesifat-sifat. Sekiranya tidak ada hujah diri, kelas yang sama tidak dapat menyimpan maklumat untuk kedua-dua objek ini.

Walau bagaimanapun, kerana kelas ini hanya cetak biru, selfmembolehkan akses ke atribut dan kaedah setiap objek di python. Ini membolehkan setiap objek mempunyai atribut dan kaedah tersendiri. Oleh itu, bahkan sebelum membuat objek ini, kita merujuk objek sebagai selfsemasa menentukan kelas.

Mengapa diri didefinisikan secara eksplisit setiap masa?

Walaupun kita memahami penggunaannya self, mungkin masih terasa aneh, terutama bagi pengaturcara yang berasal dari bahasa lain, yang selfditeruskan sebagai parameter secara eksplisit setiap kali kita menentukan kaedah. Semasa The Zen of Python pergi, " Eksplisit lebih baik daripada yang tersirat ".

Jadi, mengapa kita perlu melakukan ini? Mari kita ambil contoh mudah untuk memulakannya. Kami mempunyai Pointkelas yang menentukan kaedah distanceuntuk mengira jarak dari asal.

 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

Mari kita mulakan kelas ini dan cari jarak.

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

Dalam contoh di atas, __init__()mendefinisikan tiga parameter tetapi kita hanya melewati dua (6 dan 8). Begitu juga distance()memerlukan satu tetapi sifar hujah diluluskan. Mengapa Python tidak mengadu tentang ketidakcocokan nombor argumen ini?

Apa yang berlaku secara dalaman?

Point.distancedan p1.distancedalam contoh di atas berbeza dan tidak sama persis.

 >>> type(Point.distance) >>> type(p1.distance) 

Kita dapat melihat bahawa yang pertama adalah fungsi dan yang kedua adalah kaedah. Satu perkara yang pelik mengenai kaedah (dalam Python) adalah bahawa objek itu sendiri diteruskan sebagai argumen pertama ke fungsi yang sesuai.

Dalam kes contoh di atas, kaedah panggilan p1.distance()sebenarnya sama dengan Point.distance(p1).

Umumnya, ketika kita memanggil kaedah dengan beberapa argumen, fungsi kelas yang sesuai dipanggil dengan meletakkan objek metode sebelum argumen pertama. Jadi, apa-apa seperti obj.meth(args)menjadi Class.meth(obj, args). Proses panggilan adalah automatik sementara proses penerimaan tidak (yang eksplisit).

Ini adalah sebab parameter pertama fungsi dalam kelas mestilah objek itu sendiri. Menulis parameter selfini hanyalah konvensyen. Itu bukan kata kunci dan tidak mempunyai makna khas dalam Python. Kami boleh menggunakan nama lain (seperti this) tetapi sangat tidak digalakkan. Menggunakan nama selain daripada selfdisukai oleh kebanyakan pembangun dan menurunkan kebolehbacaan kod ( Kira kebolehbacaan ).

Diri Boleh Dielakkan

Sekarang anda sudah jelas bahawa objek (contoh) itu sendiri diserahkan sebagai argumen pertama, secara automatik. Tingkah laku tersirat ini dapat dielakkan semasa membuat kaedah statik . Pertimbangkan contoh mudah berikut:

 class A(object): @staticmethod def stat_meth(): print("Look no self was passed")

Di sini, @staticmethodadalah penghias fungsi yang menjadikannya stat_meth()statik. Mari kita mulakan kelas ini dan sebut kaedahnya.

 >>> a = A() >>> a.stat_meth() Look no self was passed

Dari contoh di atas, kita dapat melihat bahawa tingkah laku tersirat melewati objek sebagai argumen pertama dielakkan semasa menggunakan kaedah statik. Secara keseluruhan, kaedah statik berkelakuan seperti fungsi lama biasa (Oleh kerana semua objek kelas berkongsi kaedah statik).

 >>> type(A.stat_meth) >>> type(a.stat_meth) 

Diri Di Sini Untuk Menginap

Eksplisitnya selftidak unik bagi Python. Idea ini dipinjam dari Modula-3 . Berikut adalah kes penggunaan di mana ia menjadi berguna.

Tidak ada pernyataan pemboleh ubah eksplisit di Python. Mereka beraksi pada tugas pertama. Penggunaan selfmenjadikannya lebih mudah untuk membezakan antara atribut contoh (dan kaedah) dari pemboleh ubah tempatan.

Pada contoh pertama, self.x adalah atribut instance sedangkan x adalah pemboleh ubah tempatan. Mereka tidak sama dan mereka terletak di ruang nama yang berbeza.

Banyak yang mencadangkan untuk menjadikan diri sendiri kata kunci dalam Python, seperti thisdi C ++ dan Java. Ini akan menghilangkan penggunaan eksplisit yang berlebihan selfdari senarai parameter formal dalam kaedah.

Walaupun idea ini nampaknya menjanjikan, itu tidak akan berlaku. Sekurang-kurangnya tidak dalam masa terdekat. Sebab utama adalah keserasian ke belakang. Berikut adalah blog dari pencipta Python sendiri yang menerangkan mengapa diri yang eksplisit harus kekal.

__init __ () bukan pembina

Satu kesimpulan penting yang dapat diambil dari maklumat setakat ini adalah bahawa __init__()kaedah itu bukan pembina. Banyak pengaturcara Python yang naif bingung dengannya sejak __init__()dipanggil ketika kita membuat objek.

A closer inspection will reveal that the first parameter in __init__() is the object itself (object already exists). The function __init__() is called immediately after the object is created and is used to initialize it.

Technically speaking, a constructor is a method which creates the object itself. In Python, this method is __new__(). A common signature of this method is:

 __new__(cls, *args, **kwargs)

When __new__() is called, the class itself is passed as the first argument automatically(cls).

Again, like self, cls is just a naming convention. Furthermore, *args and **kwargs are used to take an arbitrary number of arguments during method calls in Python.

Some important things to remember when implementing __new__() are:

  • __new__() is always called before __init__().
  • First argument is the class itself which is passed implicitly.
  • Always return a valid object from __new__(). Not mandatory, but its main use is to create and return an object.

Let's take a look at an example:

 class Point(object): def __new__(cls,*args,**kwargs): print("From new") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self,x = 0,y = 0): print("From init") self.x = x self.y = y

Now, let's now instantiate it.

 >>> p2 = Point(3,4) From new (3, 4) () From init

This example illustrates that __new__() is called before __init__(). We can also see that the parameter cls in __new__() is the class itself (Point). Finally, the object is created by calling the __new__() method on object base class.

In Python, object is the base class from which all other classes are derived. In the above example, we have done this using super().

Use __new__ or __init__?

You might have seen __init__() very often but the use of __new__() is rare. This is because most of the time you don't need to override it. Generally, __init__() is used to initialize a newly created object while __new__() is used to control the way an object is created.

We can also use __new__() to initialize attributes of an object, but logically it should be inside __init__().

One practical use of __new__(), however, could be to restrict the number of objects created from a class.

Suppose we wanted a class SqPoint for creating instances to represent the four vertices of a square. We can inherit from our previous class Point (the second example in this article) and use __new__() to implement this restriction. Here is an example to restrict a class to have only four instances.

 class SqPoint(Point): MAX_Inst = 4 Inst_created = 0 def __new__(cls,*args,**kwargs): if (cls.Inst_created>= cls.MAX_Inst): raise ValueError("Cannot create more objects") cls.Inst_created += 1 return super().__new__(cls)

Contoh dijalankan:

 >>> p1 = SqPoint(0,0) >>> p2 = SqPoint(1,0) >>> p3 = SqPoint(1,1) >>> p4 = SqPoint(0,1) >>> >>> p5 = SqPoint(2,2) Traceback (most recent call last):… ValueError: Cannot create more objects

Artikel menarik...