9. Kelas-kelas
**************

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 menjadi "True" hanya jika
  "obj.__class__" adalah "int" atau beberapa kelas yang diturunkan
  dari "int".

* Use "issubclass()" to check class inheritance: "issubclass(bool,
  int)" is "True" since "bool" is a subclass of "int".  However,
  "issubclass(unicode, str)" is "False" since "unicode" is not a
  subclass of "str" (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 class*es, 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:*Generator*s 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*.
