9. Classes¶
Dibandingkan dengan bahasa pemrograman lain, mekanisme kelas Python menambah kelas dengan minimum sintaksis dan semantik baru. Ini adalah campuran dari mekanisme kelas yang ditemukan dalam C++ dan Modula-3. Kelas Python menyediakan semua fitur standar Pemrograman Berorientasi Objek: mekanisme pewarisan kelas memungkinkan beberapa kelas dasar, kelas turunan dapat menimpa metode apa pun dari kelas dasar atau kelasnya, dan metode dapat memanggil metode kelas dasar dengan nama yang sama . Objek dapat berisi jumlah dan jenis data yang berubah-ubah. Seperti halnya untuk modul, kelas mengambil bagian dari sifat dinamis Python: mereka dibuat pada saat runtime, dan dapat dimodifikasi lebih lanjut setelah pembuatan.
Dalam terminologi C++, biasanya anggota kelas (termasuk anggota data) adalah public (kecuali lihat di bawah Private Variables and Class-local References), dan semua fungsi anggota adalah virtual. Seperti dalam Modula-3, tidak ada singkatan untuk merujuk anggota objek dari metodenya: fungsi metode dideklarasikan dengan argumen pertama eksplisit yang mewakili objek, yang diberikan secara implisit oleh panggilan. Seperti dalam Smalltalk, kelas itu sendiri adalah objek. Ini memberikan semantik untuk mengimpor dan mengganti nama. Tidak seperti C++ dan Modula-3, tipe bawaan dapat digunakan sebagai kelas dasar untuk ekstensi oleh pengguna. Juga, seperti di C++, sebagian besar operator bawaan dengan sintaks khusus (operator aritmatika, subscripting dll) dapat didefinisikan ulang untuk instance kelas.
(Kurangnya terminologi yang diterima secara universal untuk berbicara tentang kelas, saya akan sesekali menggunakan istilah Smalltalk dan C++. Saya akan menggunakan istilah Modula-3, karena semantik berorientasi objeknya lebih dekat dengan Python daripada C++, tapi saya berharap bahwa beberapa pembaca pernah mendengarnya.)
9.1. Sepatah Kata Tentang Nama dan Objek¶
Objek memiliki individualitas, dan banyak nama (dalam berbagai lingkup) dapat terikat ke objek yang sama. Ini dikenal sebagai aliasing dalam bahasa lain. Ini biasanya tidak dihargai pada pandangan pertama pada Python, dan dapat diabaikan dengan aman ketika berhadapan dengan tipe dasar yang tidak dapat diubah (angka, string, tuple). Namun, aliasing memiliki efek yang mungkin mengejutkan pada semantik kode Python yang melibatkan objek yang bisa berubah seperti daftar list, kamus dictionary, dan sebagian besar jenis lainnya. Ini biasanya digunakan untuk kepentingan program, karena alias berperilaku seperti pointers dalam beberapa hal. Sebagai contoh, melewatkan objek adalah murah karena hanya sebuah pointer dilewatkan oleh implementasi; dan jika suatu fungsi memodifikasi objek yang dilewatkan sebagai argumen, pemanggil akan melihat perubahan --- ini menghilangkan kebutuhan untuk dua mekanisme yang berbeda melewatkan argumen argument passing seperti dalam Pascal.
9.2. Lingkup Python dan Namespaces¶
Sebelum memperkenalkan kelas, pertama-tama saya harus memberi tahu Anda tentang aturan ruang lingkup scope Python. Definisi kelas memainkan beberapa trik rapi dengan ruang nama namespaces, dan Anda perlu tahu bagaimana ruang lingkup dan ruang nama namespaces bekerja untuk sepenuhnya memahami apa yang terjadi. Kebetulan, pengetahuan tentang subjek ini berguna untuk programmer Python tingkat lanjut.
Mari kita mulai dengan beberapa definisi.
Sebuah namespace adalah pemetaan dari nama ke objek. Sebagian besar ruang nama namespace saat ini diimplementasikan sebagai kamus dictionary Python, tetapi itu biasanya tidak terlihat dengan cara apa pun (kecuali untuk kinerja), dan itu mungkin berubah di masa depan. Contoh ruang nama namespace adalah: himpunan nama bawaan (berisi fungsi seperti abs()
, dan nama pengecualian bawaan); nama-nama global dalam sebuah modul; dan nama-nama lokal dalam pemanggilan fungsi. Dalam arti himpunan atribut suatu objek juga membentuk namespace. Hal penting yang perlu diketahui tentang ruang nama namespace adalah sama sekali tidak ada hubungan antara nama dalam ruang nama namespace yang berbeda; misalnya, dua modul yang berbeda dapat mendefinisikan fungsi maximize
tanpa kebingungan --- pengguna modul harus memberikan awalan dengan nama modul.
Ngomong-ngomong, saya menggunakan kata attribute untuk nama apa pun yang mengikuti titik --- misalnya, dalam ekspresi z.real
, real
adalah atribut dari objek z
. Sebenarnya, referensi ke nama dalam modul adalah referensi atribut: dalam ekspresi modname.funcname
, modname
adalah objek modul dan funcname
adalah atributnya. Dalam kasus ini akan terjadi pemetaan langsung antara atribut modul dan nama global yang didefinisikan dalam modul: mereka berbagi namespace yang sama! 1
Atribut dapat baca-saja read-only atau dapat ditulis. Dalam kasus terakhir, pemberian nilai ke atribut dimungkinkan. Atribut modul dapat ditulis: Anda dapat menulis modname.the_answer = 42
. Atribut yang dapat ditulis juga dapat dihapus dengan pernyataan del
. Sebagai contoh, del modname.the_answer
akan menghapus atribut the_answer
dari objek yang dinamai oleh modname
.
Namespaces are created at different moments and have different lifetimes. The
namespace containing the built-in names is created when the Python interpreter
starts up, and is never deleted. The global namespace for a module is created
when the module definition is read in; normally, module namespaces also last
until the interpreter quits. The statements executed by the top-level
invocation of the interpreter, either read from a script file or interactively,
are considered part of a module called __main__
, so they have their own
global namespace. (The built-in names actually also live in a module; this is
called __builtin__
.)
Namespace lokal untuk suatu fungsi dibuat ketika fungsi dipanggil, dan dihapus ketika fungsi kembali returns atau memunculkan pengecualian yang tidak ditangani dalam fungsi tersebut. (Sebenarnya, melupakan akan menjadi cara yang lebih baik untuk menggambarkan apa yang sebenarnya terjadi.) Tentu saja, pemanggilan rekursif masing-masing memiliki ruang-nama namespace lokal mereka sendiri.
Suatu scope adalah wilayah tekstual dari program Python di mana namespace dapat diakses secara langsung. "Directly accessible" di sini berarti bahwa referensi yang tidak memenuhi syarat untuk suatu nama berusaha menemukan nama tersebut di namespace.
Meskipun cakupan scopes ditentukan secara statis, mereka digunakan secara dinamis. Setiap saat selama eksekusi, setidaknya ada tiga cakupan bersarang yang ruang nama-nya namespaces dapat diakses secara langsung:
ruang lingkup scope terdalam, yang dicari pertama kali, berisi nama-nama lokal
lingkup scope dari setiap fungsi penutup, yang dicari dimulai dengan lingkup penutup terdekat, berisi nama-nama non-lokal, tetapi juga non-global
lingkup berikutnya next-to-last berisi nama global modul saat ini
ruang lingkup scope terluar (dicari terakhir) adalah namespace yang mengandung nama bawaan
If a name is declared global, then all references and assignments go directly to the middle scope containing the module's global names. Otherwise, all variables found outside of the innermost scope are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).
Biasanya, cakupan lokal merujuk nama lokal dari fungsi (secara tekstual) saat ini. Fungsi luar, lingkup lokal merujuk namespace yang sama dengan lingkup global: namespace modul. Definisi kelas menempatkan namespace lain dalam lingkup lokal.
Penting untuk menyadari bahwa cakupan scope ditentukan secara tekstual: ruang lingkup global dari suatu fungsi yang didefinisikan dalam modul adalah ruang nama namespace modul itu, tidak peduli dari mana atau oleh apa alias fungsi itu dipanggil. Di sisi lain, pencarian nama sebenarnya dilakukan secara dinamis, pada saat run time --- namun, definisi bahasa berkembang menuju resolusi nama statis, pada waktu "compile", jadi jangan mengandalkan resolusi nama dinamis! (Faktanya, variabel lokal sudah ditentukan secara statis.)
A special quirk of Python is that -- if no global
statement is in
effect -- assignments to names always go into the innermost scope. Assignments
do not copy data --- they just bind names to objects. The same is true for
deletions: the statement del x
removes the binding of x
from the
namespace referenced by the local scope. In fact, all operations that introduce
new names use the local scope: in particular, import
statements and
function definitions bind the module or function name in the local scope. (The
global
statement can be used to indicate that particular variables
live in the global scope.)
9.3. Pandangan Pertama tentang Kelas¶
Kelas memperkenalkan sedikit sintaks baru, tiga tipe objek baru, dan beberapa semantik baru.
9.3.1. Sintaks Definisi Kelas¶
Bentuk definisi kelas paling sederhana terlihat seperti ini:
class ClassName:
<statement-1>
.
.
.
<statement-N>
Definisi kelas, seperti definisi fungsi (pernyataan def
) harus dieksekusi sebelum mereka memiliki efek. (Anda dapat menempatkan definisi kelas di cabang dari pernyataan if
, atau di dalam suatu fungsi.)
Dalam praktiknya, pernyataan di dalam definisi kelas biasanya akan menjadi definisi fungsi, tetapi pernyataan lain diizinkan, dan terkadang berguna --- kami akan kembali ke sini nanti. Definisi fungsi di dalam kelas biasanya memiliki bentuk khusus daftar argumen, didikte oleh konvensi pemanggilan untuk metode --- sekali lagi, ini dijelaskan nanti.
Ketika definisi kelas dimasukkan, namespace baru dibuat, dan digunakan sebagai lingkup scope lokal --- dengan demikian, semua tugas untuk variabel lokal masuk ke namespace baru ini. Secara khusus, definisi fungsi mengikat nama fungsi baru di sini.
Ketika definisi kelas dibiarkan normal (melalui akhir), class object dibuat. Ini pada dasarnya adalah pembungkus di sekitar isi namespace yang dibuat oleh definisi kelas; kita akan belajar lebih banyak tentang objek kelas di bagian selanjutnya. Lingkup scope lokal asli (yang berlaku tepat sebelum definisi kelas dimasukkan) diaktifkan kembali, dan objek kelas terikat di sini dengan nama kelas yang diberikan dalam header definisi kelas (ClassName
dalam contoh).
9.3.2. Objek Kelas Class Objects¶
Objek kelas mendukung dua jenis operasi: referensi atribut dan instansiasi.
Attribute references menggunakan sintaks standar yang digunakan untuk semua referensi atribut dalam Python: obj.name
. Nama atribut yang valid adalah semua nama yang ada di namespace kelas saat objek kelas dibuat. Jadi, jika definisi kelas tampak seperti ini:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
kemudian MyClass.i
dan MyClass.f
adalah referensi atribut yang valid, masing-masing mengembalikan integer dan objek fungsi. Atribut kelas juga dapat ditetapkan, sehingga Anda dapat mengubah nilai MyClass.i
oleh penugasan. __doc__
juga merupakan atribut yang valid, mengembalikan docstring milik kelas: "A simple example class"
.
instantiation kelas menggunakan notasi fungsi. Hanya berpura-pura bahwa objek kelas adalah fungsi tanpa parameter yang mengembalikan instance baru dari kelas. Misalnya (dengan asumsi kelas di atas):
x = MyClass()
membuat instance baru dari kelas dan menetapkan objek ini ke variabel lokal x
.
Operasi instansiasi ("calling" objek kelas) membuat objek kosong. Banyak kelas suka membuat objek dengan instance yang disesuaikan dengan kondisi awal tertentu. Oleh karena itu sebuah kelas dapat mendefinisikan metode khusus bernama __init__()
, seperti ini:
def __init__(self):
self.data = []
Ketika sebuah kelas mendefinisikan metode __init__()
, instantiasi kelas secara otomatis memanggil __init__()
untuk instance kelas yang baru dibuat. Jadi dalam contoh ini, contoh baru yang diinisialisasi dapat diperoleh oleh:
x = MyClass()
Tentu saja, metode __init__()
mungkin memiliki argumen untuk fleksibilitas yang lebih besar. Dalam hal itu, argumen yang diberikan kepada operator instantiasi kelas diteruskan ke __init__()
. Sebagai contoh,
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
9.3.3. Objek Instance¶
Sekarang apa yang bisa kita lakukan dengan objek instan? Satu-satunya operasi yang dipahami oleh objek instan adalah referensi atribut. Ada dua jenis nama atribut yang valid, atribut data, dan metode.
data attributes sesuai dengan "variabel instan" di Smalltalk, dan "data members" di C++. Atribut data tidak perlu dinyatakan; seperti variabel lokal, mereka muncul ketika mereka pertama kali ditugaskan. Misalnya, jika x
adalah turunan dari MyClass
yang dibuat di atas, bagian kode berikut akan mencetak nilai 16
, tanpa meninggalkan jejak:
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print x.counter
del x.counter
Jenis lain dari referensi atribut instance adalah method. Metode adalah fungsi yang "milik" suatu objek. (Dalam Python, istilah metode tidak unik untuk instance kelas: tipe objek lain dapat memiliki metode juga. Misalnya, objek daftar memiliki metode yang disebut append, insert, remove, sort, dan sebagainya. Namun, dalam diskusi berikut, kita akan menggunakan istilah metode secara eksklusif untuk mengartikan metode objek instance kelas, kecuali dinyatakan secara eksplisit.)
Nama metode yang valid dari objek instance bergantung pada kelasnya. Menurut definisi, semua atribut dari kelas yang merupakan objek fungsi menentukan metode yang sesuai dari instance-nya. Jadi dalam contoh kita, x.f
adalah referensi metode yang valid, karena MyClass.f
adalah fungsi, tetapi x.i
tidak, karena MyClass.i
tidak. Tetapi x.f
bukan hal yang sama dengan MyClass.f
--- itu adalah method object, bukan objek fungsi.
9.3.4. Metode Objek¶
Biasanya, metode dipanggil tepat setelah itu terikat:
x.f()
Dalam contoh MyClass
, ini akan mengembalikan string 'hello world'
. Namun, tidak perlu memanggil metode segera: x.f
adalah metode objek, dan dapat disimpan dan dipanggil di lain waktu. Sebagai contoh:
xf = x.f
while True:
print xf()
akan terus mencetak hello world
hingga akhir waktu.
Apa yang sebenarnya terjadi ketika suatu metode dipanggil? Anda mungkin telah memperhatikan bahwa x.f()
dipanggil tanpa argumen di atas, meskipun definisi fungsi untuk f()
menentukan argumen. Apa yang terjadi dengan argumen itu? Tentunya Python memunculkan pengecualian ketika fungsi yang membutuhkan argumen dipanggil tanpa --- bahkan jika argumen tersebut tidak benar-benar digunakan...
Actually, you may have guessed the answer: the special thing about methods is
that the object is passed as the first argument of the function. In our
example, the call x.f()
is exactly equivalent to MyClass.f(x)
. In
general, calling a method with a list of n arguments is equivalent to calling
the corresponding function with an argument list that is created by inserting
the method's object before the first argument.
Jika Anda masih tidak mengerti bagaimana metode bekerja, melihat implementasi mungkin dapat mengklarifikasi masalah. Ketika atribut non-data dari sebuah instance direferensikan, kelas instance tersebut dicari. Jika nama menunjukkan atribut kelas yang valid yang merupakan objek fungsi, objek metode dibuat dengan mengemas (menunjuk pointers ke) objek instance dan objek fungsi yang baru saja ditemukan bersama dalam objek abstrak: ini adalah objek metode. Ketika objek metode dipanggil dengan daftar argumen, daftar argumen baru dibangun dari objek instance dan daftar argumen, dan objek fungsi dipanggil dengan daftar argumen baru ini.
9.3.5. Variabel Kelas dan Instance¶
Secara umum, variabel instance adalah untuk data unik untuk setiap instance dan variabel kelas adalah untuk atribut dan metode yang dibagikan oleh semua instance kelas:
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
Seperti yang dibahas dalam Sepatah Kata Tentang Nama dan Objek, data bersama dapat memiliki efek yang mengejutkan dengan melibatkan objek mutable seperti daftar lists dan kamus dictionaries. Sebagai contoh, daftar tricks dalam kode berikut tidak boleh digunakan sebagai variabel kelas karena hanya satu daftar yang akan dibagikan oleh semua Dog instance:
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
Desain kelas yang benar harus menggunakan variabel instance sebagai gantinya:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
9.4. Keterangan Acak¶
Data attributes override method attributes with the same name; to avoid accidental name conflicts, which may cause hard-to-find bugs in large programs, it is wise to use some kind of convention that minimizes the chance of conflicts. Possible conventions include capitalizing method names, prefixing data attribute names with a small unique string (perhaps just an underscore), or using verbs for methods and nouns for data attributes.
Atribut data dapat dirujuk oleh metode dan juga oleh pengguna biasa ("clients") dari suatu objek. Dengan kata lain, kelas tidak dapat digunakan untuk mengimplementasikan tipe data abstrak murni. Faktanya, tidak ada dalam Python yang memungkinkan untuk menegakkan enforce data yang disembunyikan --- semuanya didasarkan pada konvensi. (Di sisi lain, implementasi Python, ditulis dalam C, dapat sepenuhnya menyembunyikan detail implementasi dan mengontrol akses ke objek jika perlu; ini dapat digunakan oleh ekstensi ke Python yang ditulis dalam C.)
Klien harus menggunakan atribut data dengan hati-hati --- klien dapat mengacaukan invarian yang dikelola oleh metode dengan menginjak stamping atribut data mereka. Perhatikan bahwa klien dapat menambahkan atribut data mereka sendiri ke objek instance tanpa memengaruhi validitas metode, asalkan konflik nama dihindari --- sekali lagi, konvensi penamaan dapat menghindarkan dari banyak sakit kepala di sini.
Tidak ada istilah untuk referensi atribut data (atau metode lain!) dari dalam metode. Saya menemukan bahwa ini sebenarnya meningkatkan keterbacaan metode: tidak ada kemungkinan membingungkan variabel lokal dan variabel instance ketika melirik glancing melalui metode.
Seringkali, argumen pertama dari suatu metode disebut self
. Ini tidak lebih dari sebuah konvensi: nama self
sama sekali tidak memiliki arti khusus untuk Python. Perhatikan, bagaimanapun, bahwa dengan tidak mengikuti konvensi kode Anda mungkin kurang dapat dibaca oleh programmer Python lain, dan juga dapat dibayangkan bahwa program class browser dapat ditulis yang bergantung pada konvensi semacam itu.
Objek fungsi apa pun yang merupakan atribut kelas menentukan metode untuk instance dari kelas itu. Tidak perlu bahwa definisi fungsi tertutup secara teks dalam definisi kelas: menetapkan objek fungsi ke variabel lokal di kelas juga ok. Sebagai contoh:
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)
class C:
f = f1
def g(self):
return 'hello world'
h = g
Sekarang f
, g
dan h
adalah semua atribut class C
yang merujuk ke objek-objek fungsi, dan akibatnya semuanya adalah metode instance dari C
--- h
sama persis dengan g
. Perhatikan bahwa praktik ini biasanya hanya membingungkan pembaca program.
Metode dapat memanggil metode lain dengan menggunakan atribut metode dari argumen self
:
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
Metode dapat merujuk nama global dengan cara yang sama seperti fungsi biasa. Ruang lingkup scope global yang terkait dengan suatu metode adalah modul yang berisi definisinya. (Kelas tidak pernah digunakan sebagai ruang lingkup scope global.) Sementara seseorang jarang menemukan alasan yang baik untuk menggunakan data global dalam suatu metode, ada banyak penggunaan sah lingkup global: untuk satu hal, fungsi dan modul yang diimpor ke dalam lingkup global dapat digunakan oleh metode, serta fungsi dan kelas yang didefinisikan di dalamnya. Biasanya, kelas yang berisi metode itu sendiri didefinisikan dalam lingkup global ini, dan di bagian selanjutnya kita akan menemukan beberapa alasan bagus mengapa suatu metode ingin merujuk kelasnya sendiri.
Setiap nilai adalah objek, dan karenanya memiliki kelas (juga disebut sebagai type). Ini disimpan sebagai object.__class__
.
9.5. Pewarisan¶
Tentu saja, fitur bahasa tidak akan layak untuk nama "class" tanpa mendukung pewarisan. Sintaks untuk definisi kelas turunan terlihat seperti ini:
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
Nama BaseClassName
harus didefinisikan dalam lingkup yang berisi definisi kelas turunan. Di tempat nama kelas dasar, ekspresi berubah-ubah arbitrary lainnya juga diperbolehkan. Ini bisa berguna, misalnya, ketika kelas dasar didefinisikan dalam modul lain:
class DerivedClassName(modname.BaseClassName):
Eksekusi definisi kelas turunan menghasilkan sama seperti untuk kelas dasar. Ketika objek kelas dibangun, kelas dasar diingat. Ini digunakan untuk menyelesaikan referensi atribut: jika atribut yang diminta tidak ditemukan di kelas, pencarian dilanjutkan untuk mencari di kelas dasar. Aturan ini diterapkan secara rekursif jika kelas dasar itu sendiri berasal dari beberapa kelas lain.
Tidak ada yang istimewa tentang instance kelas turunan: DerivedClassName()
membuat instance baru dari kelas. Referensi metode diselesaikan sebagai berikut: atribut kelas yang sesuai dicari, turun rantai kelas dasar jika perlu, dan referensi metode ini valid jika ini menghasilkan objek fungsi.
Kelas turunan dapat menimpa metode kelas dasar mereka. Karena metode tidak memiliki hak khusus ketika memanggil metode lain dari objek yang sama, metode kelas dasar yang memanggil metode lain yang didefinisikan dalam kelas dasar yang sama mungkin akhirnya memanggil metode kelas turunan yang menimpanya. (Untuk programmer C++: semua metode dalam Python secara efektif virtual
.)
Menimpa metode dalam kelas turunan mungkin sebenarnya ingin memperluas daripada hanya mengganti metode kelas dasar dengan nama yang sama. Ada cara sederhana untuk memanggil metode kelas dasar secara langsung: cukup panggil BaseClassName.methodname(self, arguments)
. Ini kadang-kadang berguna untuk klien juga. (Perhatikan bahwa ini hanya berfungsi jika kelas dasar dapat diakses sebagai BaseClassName
dalam lingkup global.)
Python memiliki dua fungsi bawaan yang bekerja dengan warisan:
Gunakan
isinstance()
untuk memeriksa jenis instance:isinstance(obj, int)
akan menjadiTrue
hanya jikaobj.__class__
adalahint
atau beberapa kelas yang diturunkan dariint
.Use
issubclass()
to check class inheritance:issubclass(bool, int)
isTrue
sincebool
is a subclass ofint
. However,issubclass(unicode, str)
isFalse
sinceunicode
is not a subclass ofstr
(they only share a common ancestor,basestring
).
9.5.1. Pewarisan Berganda¶
Python supports a limited form of multiple inheritance as well. A class definition with multiple base classes looks like this:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
For old-style classes, the only rule is depth-first, left-to-right. Thus, if an
attribute is not found in DerivedClassName
, it is searched in
Base1
, then (recursively) in the base classes of Base1
, and
only if it is not found there, it is searched in Base2
, and so on.
(To some people breadth first --- searching Base2
and Base3
before the base classes of Base1
--- looks more natural. However, this
would require you to know whether a particular attribute of Base1
is
actually defined in Base1
or in one of its base classes before you can
figure out the consequences of a name conflict with an attribute of
Base2
. The depth-first rule makes no differences between direct and
inherited attributes of Base1
.)
For new-style classes, the method resolution order changes dynamically
to support cooperative calls to super()
. This approach is known in some
other multiple-inheritance languages as call-next-method and is more powerful
than the super call found in single-inheritance languages.
With new-style classes, dynamic ordering is necessary because all cases of
multiple inheritance exhibit one or more diamond relationships (where at
least one of the parent classes can be accessed through multiple paths from the
bottommost class). For example, all new-style classes inherit from
object
, so any case of multiple inheritance provides more than one path
to reach object
. To keep the base classes from being accessed more
than once, the dynamic algorithm linearizes the search order in a way that
preserves the left-to-right ordering specified in each class, that calls each
parent only once, and that is monotonic (meaning that a class can be subclassed
without affecting the precedence order of its parents). Taken together, these
properties make it possible to design reliable and extensible classes with
multiple inheritance. For more detail, see
https://www.python.org/download/releases/2.3/mro/.
9.6. Private Variables and Class-local References¶
Variabel instance "Private" yang tidak dapat diakses kecuali dari dalam suatu objek tidak ada dalam Python. Namun, ada konvensi yang diikuti oleh sebagian besar kode Python: nama diawali dengan garis bawah (mis. _spam
) harus diperlakukan sebagai bagian non-publik dari API (apakah itu fungsi, metode atau anggota data). Ini harus dianggap sebagai detail implementasi dan dapat berubah tanpa pemberitahuan.
Karena ada kasus penggunaan yang valid untuk anggota kelas-pribadi (yaitu untuk menghindari bentrokan nama dengan nama yang ditentukan oleh subkelas), ada dukungan terbatas untuk mekanisme semacam itu, yang disebut name mangling. Setiap pengidentifikasi dari bentuk __spam
(setidaknya dua garis bawah utama, paling banyak satu garis bawah garis bawah) secara teks diganti dengan _classname__spam
, di mana classname
adalah nama kelas saat ini dengan garis(-garis) bawah utama dilucuti. Mangling ini dilakukan tanpa memperhatikan posisi sintaksis pengidentifikasi, asalkan terjadi dalam definisi kelas.
Name mangling sangat membantu untuk membiarkan subclass menimpa metode tanpa memutus panggilan metode intraclass. Sebagai contoh:
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
Contoh di atas akan berfungsi bahkan jika MappingSubclass
akan memperkenalkan sebuah pengidentifikasi __update
karena diganti dengan _Mapping__update
di kelas Mapping
dan _MappingSubclass__update
di kelas MappingSubclass
masing-masing.
Perhatikan bahwa aturan mangling sebagian besar dirancang untuk menghindari kecelakaan; masih dimungkinkan untuk mengakses atau memodifikasi variabel yang dianggap pribadi. Ini bahkan dapat berguna dalam keadaan khusus, seperti di debugger.
Notice that code passed to exec
, eval()
or execfile()
does not
consider the classname of the invoking class to be the current class; this is
similar to the effect of the global
statement, the effect of which is
likewise restricted to code that is byte-compiled together. The same
restriction applies to getattr()
, setattr()
and delattr()
, as well
as when referencing __dict__
directly.
9.7. Barang Sisa Odds and Ends¶
Kadang-kadang berguna untuk memiliki tipe data yang mirip dengan "record" Pascal atau "struct" C, menyatukan beberapa item data bernama. Definisi kelas kosong akan menghasilkan hal tersebut dengan baik:
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
Sepotong kode Python yang mengharapkan tipe data abstrak tertentu sering dapat dilewatkan kelas yang mengemulasi metode tipe data itu sebagai gantinya. Misalnya, jika Anda memiliki fungsi yang memformat beberapa data dari objek file, Anda dapat mendefinisikan kelas dengan metode read()
dan readline()
yang mendapatkan data dari buffer string sebagai gantinya, dan meneruskan itu sebagai argumen.
Instance method objects have attributes, too: m.im_self
is the instance
object with the method m()
, and m.im_func
is the function object
corresponding to the method.
9.8. Exceptions Are Classes Too¶
User-defined exceptions are identified by classes as well. Using this mechanism it is possible to create extensible hierarchies of exceptions.
There are two new valid (semantic) forms for the raise
statement:
raise Class, instance
raise instance
In the first form, instance
must be an instance of Class
or of a
class derived from it. The second form is a shorthand for:
raise instance.__class__, instance
A class in an except
clause is compatible with an exception if it is
the same class or a base class thereof (but not the other way around --- an
except clause listing a derived class is not compatible with a base class). For
example, the following code will print B, C, D in that order:
class B:
pass
class C(B):
pass
class D(C):
pass
for c in [B, C, D]:
try:
raise c()
except D:
print "D"
except C:
print "C"
except B:
print "B"
Note that if the except clauses were reversed (with except B
first), it
would have printed B, B, B --- the first matching except clause is triggered.
When an error message is printed for an unhandled exception, the exception's
class name is printed, then a colon and a space, and finally the instance
converted to a string using the built-in function str()
.
9.9. Iterators¶
Sekarang Anda mungkin telah memperhatikan bahwa sebagian besar objek penampung container dapat dibuat perulangan menggunakan pernyataan for
:
for element in [1, 2, 3]:
print element
for element in (1, 2, 3):
print element
for key in {'one':1, 'two':2}:
print key
for char in "123":
print char
for line in open("myfile.txt"):
print line,
This style of access is clear, concise, and convenient. The use of iterators
pervades and unifies Python. Behind the scenes, the for
statement
calls iter()
on the container object. The function returns an iterator
object that defines the method next()
which accesses elements
in the container one at a time. When there are no more elements,
next()
raises a StopIteration
exception which tells the
for
loop to terminate.
This example shows how it all works:
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
it.next()
StopIteration
Having seen the mechanics behind the iterator protocol, it is easy to add
iterator behavior to your classes. Define an __iter__()
method which
returns an object with a next()
method. If the class
defines next()
, then __iter__()
can just return self
:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
... print char
...
m
a
p
s
9.10. Pembangkit Generator¶
: term:Generators adalah alat yang sederhana dan berdaya untuk membuat iterator. Mereka ditulis seperti fungsi biasa tetapi menggunakan pernyataan yield
setiap kali mereka ingin mengembalikan data. Setiap kali next()
dipanggil, generator melanjutkan di tempat yang ditinggalkannya (ia mengingat semua nilai data dan pernyataan mana yang terakhir dieksekusi). Contoh menunjukkan bahwa pembangkit generator dapat dengan mudah dibuat:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
... print char
...
f
l
o
g
Anything that can be done with generators can also be done with class-based
iterators as described in the previous section. What makes generators so
compact is that the __iter__()
and next()
methods
are created automatically.
Fitur utama lainnya adalah variabel lokal dan status eksekusi secara otomatis disimpan di antara pemanggilan. Ini membuat fungsi lebih mudah untuk ditulis dan jauh lebih jelas daripada pendekatan menggunakan variabel instan seperti self.index
dan self.data
.
Selain pembuatan metode otomatis dan menyimpan status program, ketika pembangkit generator berhenti, mereka secara otomatis menimbulkan StopIteration
. Secara kombinasi, fitur-fitur ini membuatnya mudah untuk membuat iterator tanpa lebih dari sekadar menulis fungsi biasa.
9.11. Ekspresi Pembangkit Generator¶
Beberapa pembangkit generators sederhana dapat dikodekan secara ringkas sebagai ekspresi menggunakan sintaksis yang mirip dengan pemahaman daftar list comprehensions tetapi dengan tanda kurung bukan dengan tanda kurung siku. Ungkapan-ungkapan ini dirancang untuk situasi di mana generator digunakan segera oleh fungsi penutup. Ekspresi generator lebih kompak tetapi kurang fleksibel daripada definisi generator penuh dan cenderung lebih ramah memori daripada pemahaman daftar list comprehensions setara.
Contoh:
>>> sum(i*i for i in range(10)) # sum of squares
285
>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
260
>>> from math import pi, sin
>>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))
>>> unique_words = set(word for line in page for word in line.split())
>>> valedictorian = max((student.gpa, student.name) for student in graduates)
>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']
Catatan kaki
- 1
Kecuali satu hal. Objek modul memiliki atribut baca-saja read-only rahasia bernama
__dict__
yang mengembalikan kamus dictionary yang digunakan untuk mengimplementasikan namespace modul; nama__ dict__
adalah atribut tetapi bukan nama global. Jelas, menggunakan ini melanggar abstraksi implementasi namespace, dan harus dibatasi untuk hal-hal seperti debuggers post-mortem.