self dengan Python, Demystified

Jika Anda telah memprogram dengan Python (pemrograman berorientasi objek) untuk beberapa waktu, maka Anda pasti menemukan metode yang memiliki selfparameter pertama mereka.

Pertama-tama, mari kita coba memahami apa parameter diri yang berulang ini.

Apa itu self in Python?

Dalam pemrograman berorientasi objek, setiap kali kami mendefinisikan metode untuk kelas, kami menggunakan selfsebagai parameter pertama dalam setiap kasus. Mari kita lihat definisi kelas yang disebut 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 hal ini semua metode, termasuk __init__, memiliki parameter pertama sebagai self.

Kita tahu bahwa kelas adalah cetak biru untuk objeknya. Cetak biru ini dapat digunakan untuk membuat banyak objek. Mari buat dua objek berbeda dari kelas di atas.

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

Kata selfkunci digunakan untuk merepresentasikan sebuah instance (objek) dari kelas yang diberikan. Dalam hal ini, dua Catobjek cat1dan cat2memiliki atribut namedan mereka sendiri age. Jika tidak ada argumen mandiri, kelas yang sama tidak dapat menyimpan informasi untuk kedua objek ini.

Namun, karena kelas hanyalah cetak biru, selfmemungkinkan akses ke atribut dan metode setiap objek dalam python. Ini memungkinkan setiap objek memiliki atribut dan metodenya sendiri. Jadi, bahkan jauh sebelum membuat objek ini, kami mereferensikan objek selfsaat mendefinisikan kelas.

Mengapa diri didefinisikan secara eksplisit setiap saat?

Bahkan ketika kita memahami penggunaannya self, ini mungkin masih tampak aneh, terutama bagi programmer yang berasal dari bahasa lain, yang selfditeruskan sebagai parameter secara eksplisit setiap kali kita mendefinisikan metode. Seperti The Zen of Python , " Eksplisit lebih baik daripada implisit ".

Jadi, mengapa kita perlu melakukan ini? Mari kita ambil contoh sederhana untuk memulai. Kami memiliki Pointkelas yang mendefinisikan metode distanceuntuk menghitung 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 sekarang memberi contoh kelas ini dan mencari jarak.

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

Dalam contoh di atas, __init__()mendefinisikan tiga parameter tetapi kami baru saja melewati dua (6 dan 8). Demikian pula distance()membutuhkan satu tetapi nol argumen dilewatkan. Mengapa Python tidak mengeluh tentang ketidakcocokan nomor argumen ini?

Apa Yang Terjadi Secara Internal?

Point.distancedan p1.distancepada contoh di atas berbeda dan tidak persis sama.

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

Kita dapat melihat bahwa yang pertama adalah sebuah fungsi dan yang kedua adalah sebuah metode. Hal yang aneh tentang metode (dengan Python) adalah bahwa objek itu sendiri dilewatkan sebagai argumen pertama ke fungsi yang sesuai.

Dalam contoh di atas, pemanggilan metode p1.distance()sebenarnya sama dengan Point.distance(p1).

Umumnya, 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 haruslah objek itu sendiri. Menulis parameter selfini hanya sebagai konvensi. Ini bukan kata kunci dan tidak memiliki arti khusus dalam Python. Kami dapat menggunakan nama lain (seperti this) tetapi sangat tidak disarankan. Menggunakan nama selain selftidak disukai oleh sebagian besar pengembang dan menurunkan keterbacaan kode ( jumlah Keterbacaan ).

Diri Bisa Dihindari

Sekarang Anda sudah jelas bahwa objek (instance) itu sendiri diteruskan sebagai argumen pertama, secara otomatis. Perilaku implisit ini dapat dihindari saat membuat metode statis . Perhatikan contoh sederhana berikut:

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

Di sini, @staticmethodadalah dekorator fungsi yang membuat stat_meth()statis. Mari kita membuat instance kelas ini dan memanggil metode tersebut.

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

Dari contoh di atas, kita dapat melihat bahwa perilaku implisit dalam meneruskan objek sebagai argumen pertama dihindari saat menggunakan metode statis. Secara keseluruhan, metode statis berperilaku seperti fungsi lama biasa (Karena semua objek kelas berbagi metode statis).

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

Self Is Here To Stay

Eksplisit selftidak unik untuk Python. Ide ini dipinjam dari Modula-3 . Berikut adalah kasus penggunaan yang akan membantu.

Tidak ada deklarasi variabel eksplisit di Python. Mereka mulai bertindak pada tugas pertama. Penggunaan selfmemudahkan untuk membedakan antara atribut instance (dan metode) dari variabel lokal.

Dalam contoh pertama, self.x adalah atribut instance sedangkan x adalah variabel lokal. Mereka tidak sama dan terletak di ruang nama yang berbeda.

Banyak yang mengusulkan untuk menjadikan diri sebagai kata kunci dengan Python, seperti thisdi C ++ dan Java. Ini akan menghilangkan penggunaan eksplisit yang berlebihan dari selfdaftar parameter formal dalam metode.

Meskipun ide ini tampak menjanjikan, itu tidak akan terjadi. Setidaknya tidak dalam waktu dekat. Alasan utamanya adalah kompatibilitas ke belakang. Berikut adalah blog dari pembuat Python sendiri yang menjelaskan mengapa diri eksplisit harus tetap ada.

__init __ () bukan konstruktor

Satu kesimpulan penting yang dapat ditarik dari informasi sejauh ini adalah bahwa __init__()metode tersebut bukan konstruktor. Banyak programmer Python yang naif menjadi bingung dengannya karena __init__()dipanggil saat 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 berjalan:

 >>> 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 yang menarik...