Anda di halaman 1dari 64

MODULE 6

6.1.1 Python Essentials: Module 6


6.1.1 PYTHON ESSENTIALS: MODULE 6
6.1.1.1 Python Essentials: Module 6
Dasar-Dasar Pemrograman dalam Python: Modul 6
Dalam modul ini, Anda akan belajar tentang:
 pendekatan berorientasi objek - yayasan;
 kelas, metode, objek, dan fitur objektif standar;
 penanganan pengecualian;
 bekerja dengan file.

6.1.1.2 The foundations of OOP


Dasar pemikiran dari pendekatan berorientasi objek
Mari kita mengambil langkah di luar pemrograman komputer dan komputer secara umum, dan
membahas masalah pemrograman objek.

Hampir semua program dan teknik yang telah Anda gunakan sampai sekarang berada di bawah
gaya prosedural pemrograman. Memang, Anda telah menggunakan beberapa objek bawaan,
tetapi ketika merujuknya, kami hanya menyebutkan minimum absolut.

Gaya pemrograman prosedural adalah pendekatan dominan untuk pengembangan perangkat


lunak selama beberapa dekade TI, dan masih digunakan sampai sekarang. Selain itu, itu tidak
akan hilang di masa depan, karena ia bekerja dengan sangat baik untuk jenis proyek tertentu
(umumnya, tidak terlalu rumit dan tidak besar, tetapi ada banyak pengecualian untuk aturan
itu).

Pendekatan objek cukup muda (jauh lebih muda dari pendekatan prosedural) dan sangat
berguna ketika diterapkan pada proyek-proyek besar dan kompleks yang dilakukan oleh tim
besar yang terdiri dari banyak pengembang.

Pemahaman semacam ini tentang struktur proyek membuat banyak tugas penting lebih mudah,
misalnya, membagi proyek menjadi bagian-bagian kecil, mandiri, dan pengembangan
independen elemen-elemen proyek yang berbeda.

Python adalah alat universal untuk pemrograman objek dan prosedural . Ini mungkin berhasil
digunakan di kedua bidang.

Selain itu, Anda dapat membuat banyak aplikasi yang berguna, bahkan jika Anda tidak tahu
apa-apa tentang kelas dan objek, tetapi Anda harus ingat bahwa beberapa masalah (misalnya,
penanganan antarmuka pengguna grafis) mungkin memerlukan pendekatan objek yang ketat.
Untungnya, pemrograman objek relatif sederhana.

6.1.1.3 The foundations of OOP


Prosedural vs. pendekatan berorientasi objek
Dalam pendekatan prosedural , dimungkinkan untuk membedakan dua dunia yang berbeda
dan benar-benar terpisah: dunia data, dan dunia kode . Dunia data diisi dengan variabel dari
berbagai jenis, sedangkan dunia kode dihuni oleh kode yang dikelompokkan ke dalam modul
dan fungsi.

Fungsinya dapat menggunakan data, tetapi tidak sebaliknya. Selain itu, fungsi dapat
menyalahgunakan data, yaitu, untuk menggunakan nilai secara tidak sah (misalnya, ketika
fungsi sinus mendapatkan saldo rekening bank sebagai parameter).

Kami mengatakan di masa lalu bahwa data tidak dapat menggunakan fungsi. Tetapi apakah
ini sepenuhnya benar? Apakah ada beberapa jenis data khusus yang dapat menggunakan
fungsi?
Ya, ada - yang bernama metode. Ini adalah fungsi yang dipanggil dari dalam data, bukan di
sampingnya. Jika Anda dapat melihat perbedaan ini, Anda telah mengambil langkah pertama
ke pemrograman objek.

The pendekatan object menyarankan cara yang sama sekali berbeda pemikiran. Data dan
kode diapit bersama di dunia yang sama, dibagi menjadi beberapa kelas.

Setiap kelas seperti resep yang dapat digunakan ketika Anda ingin membuat objek yang
berguna (dari sinilah nama pendekatan berasal). Anda dapat menghasilkan objek sebanyak
yang Anda butuhkan untuk menyelesaikan masalah Anda.

Setiap objek memiliki seperangkat sifat (mereka disebut properti atau atribut - kami akan
menggunakan kedua kata secara sinonim) dan mampu melakukan serangkaian aktivitas (yang
disebut metode).

Resep dapat dimodifikasi jika tidak memadai untuk tujuan tertentu dan, pada dasarnya, kelas
baru dapat dibuat. Kelas-kelas baru ini mewarisi properti dan metode dari aslinya, dan biasanya
menambahkan beberapa yang baru, membuat alat baru yang lebih spesifik.

Objek adalah inkarnasi dari ide-ide yang diekspresikan dalam kelas, seperti cheesecake di piring
Anda adalah inkarnasi dari ide yang diekspresikan dalam resep yang dicetak dalam buku masak
lama.

Objek berinteraksi satu sama lain, bertukar data atau mengaktifkan metode mereka. Kelas yang
dibangun dengan benar (dan dengan demikian, objeknya) mampu melindungi data yang
masuk akal dan menyembunyikannya dari modifikasi yang tidak sah.

Tidak ada batas yang jelas antara data dan kode: mereka hidup sebagai satu di objek.
Semua konsep ini tidak abstrak seperti yang Anda duga pada awalnya. Sebaliknya, mereka
semua diambil dari pengalaman kehidupan nyata, dan karenanya sangat berguna dalam
pemrograman komputer: mereka tidak menciptakan kehidupan buatan - mereka
mencerminkan fakta, hubungan, dan keadaan nyata .

6.1.1.4 The foundations of OOP


Hirarki kelas
Kata kelas memiliki banyak arti, tetapi tidak semua dari mereka yang kompatibel dengan ide-ide
yang ingin kita bahas di sini. The kelas yang kita prihatin dengan seperti kategori, sebagai hasil
dari kesamaan tepat didefinisikan.

Kami akan mencoba menunjukkan beberapa kelas yang merupakan contoh bagus dari konsep
ini.
Mari kita lihat sejenak kendaraan. Semua kendaraan yang ada (dan yang belum
ada) dihubungkan oleh satu fitur penting : kemampuan untuk bergerak. Anda mungkin
berpendapat bahwa seekor anjing juga bergerak; apakah anjing itu kendaraan? Tidak. Kita
harus meningkatkan definisi, yaitu memperkaya dengan kriteria lain, membedakan kendaraan
dari makhluk lain, dan menciptakan koneksi yang lebih kuat. Mari kita pertimbangkan keadaan
berikut: kendaraan adalah entitas yang dibuat secara artifisial yang digunakan untuk
transportasi, digerakkan oleh kekuatan alam, dan diarahkan (didorong) oleh manusia.
Berdasarkan definisi ini, anjing bukan kendaraan.

Kelas kendaraan sangat luas. Terlalu luas Kita harus mendefinisikan beberapa kelas
yang lebih terspesialisasi. Kelas khusus adalah subclass. Kelas kendaraan akan
menjadi superclass bagi mereka semua.

Catatan: hierarki tumbuh dari atas ke bawah, seperti akar pohon, bukan cabang. Kelas yang
paling umum, dan terluas, selalu berada di atas (superclass) sedangkan turunannya berada di
bawah (subclass).

Sekarang, Anda mungkin dapat menunjukkan beberapa subclass potensial


untuk superclass Kendaraan. Ada banyak klasifikasi yang memungkinkan. Kami telah memilih
subclass berdasarkan lingkungan, dan mengatakan bahwa ada (setidaknya) empat subclass:
 kendaraan darat;
 kendaraan air;
 kendaraan udara;
 kendaraan luar angkasa.

Dalam contoh ini, kita akan membahas hanya subkelas pertama - kendaraan darat. Jika mau,
Anda dapat melanjutkan dengan kelas yang tersisa.
Kendaraan darat dapat dibagi lebih lanjut, tergantung pada metode yang berdampak pada
tanah. Jadi, kita dapat menghitung:
 kendaraan roda;
 kendaraan yang dilacak;
 hovercrafts.

Hirarki yang kami buat diilustrasikan oleh gambar.


Perhatikan arah panah - mereka selalu menunjuk ke superclass. Kelas tingkat atas adalah
pengecualian - tidak memiliki kelas superclass sendiri.

6.1.1.5 The foundations of OOP


Hirarki kelas: lanjutan
Contoh lain adalah hierarki kerajaan taksonomi hewan.
Kita dapat mengatakan bahwa semua hewan (kelas tingkat atas kami) dapat dibagi menjadi
lima subclass:
 mamalia;
 reptil;
 burung-burung;
 ikan;
 amfibi.
Kami akan mengambil yang pertama untuk analisis lebih lanjut.
Kami telah mengidentifikasi subclass berikut:
 mamalia liar;
 mamalia jinak.
Cobalah untuk memperluas hierarki dengan cara apa pun yang Anda inginkan, dan temukan
tempat yang tepat untuk manusia.

6.1.1.6 The foundations of OOP


Apa itu objek?
Kelas (di antara definisi lainnya) adalah sekumpulan objek. Objek adalah makhluk yang dimiliki
kelas .

Objek adalah inkarnasi dari persyaratan, sifat, dan kualitas yang ditugaskan untuk kelas
tertentu . Ini mungkin terdengar sederhana, tetapi perhatikan keadaan penting berikut ini. Kelas
membentuk hierarki. Ini dapat berarti bahwa objek milik kelas tertentu milik semua kacamata
super pada saat yang sama. Ini juga dapat berarti bahwa objek apa pun yang termasuk dalam
superclass mungkin bukan milik salah satu dari subkelasnya.

Sebagai contoh: setiap mobil pribadi adalah benda milik kelas kendaraan roda. Ini juga berarti
bahwa mobil yang sama milik semua superclasses dari kelas rumahnya; oleh karena itu, ia adalah
anggota kelas kendaraan juga. Anjing Anda (atau kucing Anda) adalah objek yang termasuk
dalam kelas mamalia peliharaan, yang secara eksplisit berarti ia termasuk
dalam kelas hewan juga.

Setiap subclass lebih terspesialisasi (atau lebih spesifik) daripada superclassnya. Sebaliknya,
setiap superclass lebih umum (lebih abstrak) daripada subkelasnya. Perhatikan bahwa kami
menduga bahwa sebuah kelas mungkin hanya memiliki satu superclass - ini tidak selalu benar,
tetapi kami akan membahas masalah ini sedikit lebih lanjut.

Warisan
Mari kita mendefinisikan salah satu konsep dasar pemrograman objek,
bernama inheritance. Objek apa pun yang terikat pada level tertentu dari hierarki kelas mewarisi
semua sifat (serta persyaratan dan kualitas) yang ditentukan di dalam salah satu superclasses.

Kelas asal objek dapat menentukan sifat-sifat baru (serta persyaratan dan kualitas) yang akan
diwarisi oleh salah satu superclasses-nya.
Anda seharusnya tidak memiliki masalah dengan mencocokkan aturan ini dengan contoh
spesifik, apakah itu berlaku untuk hewan, atau untuk kendaraan.
6.1.1.7 The foundations of OOP
Apa yang dimiliki objek?
Konvensi pemrograman objek mengasumsikan bahwa setiap objek yang ada dapat dilengkapi
dengan tiga kelompok atribut :
 suatu objek memiliki nama yang secara unik mengidentifikasinya di dalam namespace
asalnya (walaupun mungkin ada beberapa objek anonim juga)
 suatu objek memiliki sekumpulan properti individual yang membuatnya asli, unik atau luar
biasa (walaupun mungkin saja beberapa objek tidak memiliki properti sama sekali)
 suatu objek memiliki seperangkat kemampuan untuk melakukan aktivitas tertentu,
mampu mengubah objek itu sendiri, atau beberapa objek lainnya.

Ada petunjuk (walaupun ini tidak selalu berhasil) yang dapat membantu Anda mengidentifikasi
salah satu dari tiga bidang di atas. Setiap kali Anda menggambarkan suatu objek dan Anda
menggunakan:
 kata benda - Anda mungkin mendefinisikan nama objek;
 kata sifat - Anda mungkin mendefinisikan properti objek;
 kata kerja - Anda mungkin mendefinisikan aktivitas objek.

Dua frase sampel harus berfungsi sebagai contoh yang baik:


 Max adalah kucing besar yang tidur sepanjang hari.

Nama objek = Max


Kelas rumah =
Properti Kucing = Ukuran (besar)
Aktivitas = Tidur (sepanjang hari)

 Cadillac merah muda pergi dengan cepat.

Nama objek = Cadillac


Kelas rumah = Kendaraan beroda
Properti = Warna (merah muda)
Aktivitas = Pergi (cepat)
6.1.1.8 The foundations of OOP
Kelas pertama kamu
Pemrograman objek adalah seni mendefinisikan dan memperluas kelas. Kelas adalah model
dari bagian realitas yang sangat spesifik, yang mencerminkan sifat dan aktivitas yang ditemukan
di dunia nyata.

Kelas-kelas yang didefinisikan di awal terlalu umum dan tidak tepat untuk mencakup jumlah kasus
nyata yang paling besar.

Tidak ada halangan untuk mendefinisikan subclass baru yang lebih tepat. Mereka akan mewarisi
segalanya dari superclass mereka, sehingga pekerjaan yang masuk ke penciptaannya tidak sia-
sia.

Kelas baru dapat menambahkan properti baru dan aktivitas baru, dan karenanya mungkin lebih
berguna dalam aplikasi tertentu. Jelas, ini dapat digunakan sebagai superclass untuk sejumlah
subclass yang baru dibuat.

Prosesnya tidak harus berakhir. Anda dapat membuat kelas sebanyak yang Anda butuhkan.
Kelas yang Anda tetapkan tidak ada hubungannya dengan objek: keberadaan kelas tidak
berarti bahwa salah satu objek yang kompatibel akan secara otomatis dibuat. Kelas itu sendiri
tidak dapat membuat objek - Anda harus membuatnya sendiri, dan Python memungkinkan
Anda untuk melakukan ini.

Sudah waktunya untuk mendefinisikan kelas paling sederhana dan untuk membuat
objek. Lihatlah contoh di bawah ini:
class TheSimplestClass: pass

Kami telah mendefinisikan kelas di sana. Kelasnya agak buruk: tidak memiliki properti atau
aktivitas. Sebenarnya kosong, tapi itu tidak masalah untuk saat ini. Semakin sederhana kelas,
semakin baik untuk tujuan kita.

Definisi dimulai dengan kata kunciclass. Kata kunci diikuti oleh pengidentifikasi yang akan
memberi nama kelas (catatan: jangan bingung dengan nama objek - ini adalah dua hal yang
berbeda).

Selanjutnya, Anda menambahkan titik dua :), karena kelas, seperti fungsi, membentuk blok
bersarang mereka sendiri. Konten di dalam blok mendefinisikan semua properti dan aktivitas
kelas.

Kata passkunci tidak memenuhi kelas. Itu tidak mengandung metode atau properti apa pun.

Objek pertama Anda


Kelas yang baru didefinisikan menjadi alat yang mampu membuat objek baru. Alat harus
digunakan secara eksplisit, sesuai permintaan.
Bayangkan bahwa Anda ingin membuat satu (tepat satu) objek TheSimplestClasskelas.
Untuk melakukan ini, Anda perlu menetapkan variabel untuk menyimpan objek yang baru dibuat
dari kelas itu, dan membuat objek secara bersamaan.
Anda melakukannya dengan cara berikut:

myFirstObject = TheSimplestClass()

catatan:
 nama kelas mencoba untuk berpura-pura bahwa itu adalah fungsi - dapatkah Anda
melihat ini? Kami akan segera membahasnya;
 objek yang baru dibuat dilengkapi dengan semua yang dibawa oleh kelas; karena kelas
ini benar-benar kosong, objeknya juga kosong.
Tindakan membuat objek dari kelas yang dipilih juga disebut instantiation (karena objek
menjadi instance dari kelas ).
Mari kita tinggalkan kelas sendirian untuk sesaat, karena kita sekarang akan memberi tahu
Anda beberapa kata tentang tumpukan. Kita tahu konsep kelas dan objek mungkin
belum sepenuhnya jelas. Jangan khawatir, kami akan segera menjelaskan semuanya.

6.1.2.1 A short journey from procedural to object approach


Apa itu tumpukan?
Tumpukan adalah struktur yang dikembangkan untuk menyimpan data dengan cara yang
sangat spesifik . Bayangkan setumpuk koin. Anda tidak dapat meletakkan koin di tempat lain
selain di atas tumpukan. Demikian pula, Anda tidak bisa mendapatkan koin dari tumpukan dari
tempat lain selain dari atas tumpukan. Jika Anda ingin mendapatkan koin yang terletak di
bagian bawah, Anda harus menghapus semua koin dari level yang lebih tinggi.

Nama alternatif untuk stack (tetapi hanya dalam terminologi IT) adalah LIFO . Ini adalah
singkatan dari deskripsi perilaku stack yang sangat jelas: Last In - First Out . Koin yang datang
terakhir ke tumpukan akan pergi terlebih dahulu.

Tumpukan adalah objek dengan dua operasi dasar, yang secara konvensional
bernama push(ketika elemen baru diletakkan di atas) dan pop (ketika elemen yang ada diambil
dari atas).

Tumpukan sangat sering digunakan dalam banyak algoritma klasik, dan sulit untuk
membayangkan implementasi dari banyak alat yang banyak digunakan tanpa menggunakan
tumpukan.

Mari kita mengimplementasikan tumpukan dengan Python. Ini akan menjadi tumpukan yang
sangat sederhana, dan kami akan menunjukkan kepada Anda bagaimana melakukannya
dalam dua pendekatan independen: prosedural dan objektif.
Mari kita mulai dengan yang pertama.

6.1.2.2 A short journey from procedural to object approach


Tumpukan - pendekatan prosedural
Pertama, Anda harus memutuskan bagaimana menyimpan nilai yang akan tiba di stack. Kami
menyarankan menggunakan metode yang paling sederhana, dan menggunakan daftaruntuk
pekerjaan ini. Mari kita asumsikan bahwa ukuran tumpukan tidak terbatas. Mari kita asumsikan
juga bahwa elemen terakhir dari daftar menyimpan elemen teratas.

Tumpukan itu sendiri sudah dibuat:


stack = []

Kami siap untuk mendefinisikan fungsi yang memberikan nilai pada stack . Berikut adalah
anggapan untuk itu:
 nama untuk fungsinya adalah push;
 fungsi mendapat satu parameter (ini adalah nilai yang akan dimasukkan ke tumpukan)
 fungsi tidak mengembalikan apa pun;
 fungsi menambahkan nilai parameter ke akhir tumpukan;

Inilah cara kami melakukannya - lihat:


def push(val): stack.append(val)

Sekarang saatnya fungsi untuk mengambil nilai dari stack . Ini adalah bagaimana Anda dapat
melakukannya:
 nama fungsinya adalah pop;
 fungsi tidak mendapatkan parameter apa pun;
 fungsi mengembalikan nilai yang diambil dari tumpukan
 fungsi membaca nilai dari atas tumpukan dan menghilangkannya.

Fungsinya di sini:
def pop(): val = stack[-1] del stack[-1] return val

Catatan: fungsi tidak memeriksa apakah ada elemen dalam tumpukan.


Mari kita kumpulkan semua potongan untuk mengatur tumpukan
bergerak. Program lengkap mendorong tiga angka ke tumpukan, menariknya, dan mencetak
nilainya di layar. Anda dapat melihatnya di jendela editor.
Program menampilkan teks berikut ke layar:
123

Menguji.

6.1.2.3 A short journey from procedural to object approach


Tumpukan - pendekatan prosedural vs. pendekatan berorientasi objek
Tumpukan prosedural sudah siap. Tentu saja, ada beberapa kelemahan, dan implementasinya
dapat ditingkatkan dalam banyak cara (memanfaatkan pengecualian untuk bekerja adalah
ide yang bagus), tetapi secara umum stack diimplementasikan secara penuh, dan Anda dapat
menggunakannya jika perlu.

Tetapi semakin sering Anda menggunakannya, semakin banyak kerugian yang akan Anda
temui. Inilah beberapa di antaranya:
 variabel esensial (daftar tumpukan) sangat rentan ; siapa pun dapat memodifikasinya
dengan cara yang tidak terkendali, menghancurkan tumpukan, pada dasarnya; ini
tidak berarti bahwa itu dilakukan dengan jahat - sebaliknya, itu bisa terjadi sebagai
akibat dari kecerobohan, misalnya, ketika seseorang membingungkan nama-nama
variabel; bayangkan bahwa Anda secara tidak sengaja telah menulis sesuatu seperti
ini:

stack[0] = 0
Fungsi tumpukan akan sepenuhnya berantakan;

 mungkin juga terjadi bahwa suatu hari Anda membutuhkan lebih dari satu
tumpukan; Anda harus membuat daftar lain untuk penyimpanan stack ini, dan mungkin
lainnya pushdan popfungsi juga;

 mungkin juga terjadi bahwa Anda tidak hanya membutuhkan pushdan popberfungsi,
tetapi juga beberapa kemudahan lain; Anda tentu bisa menerapkannya, tetapi coba
bayangkan apa yang akan terjadi jika Anda memiliki lusinan tumpukan yang
diimplementasikan secara terpisah.

Pendekatan objektif memberikan solusi untuk masing-masing masalah di atas. Beri nama dulu:
 kemampuan untuk menyembunyikan (melindungi) nilai yang dipilih terhadap akses yang
tidak sah disebut enkapsulasi; nilai-nilai yang dienkapsulasi tidak dapat diakses atau
dimodifikasi jika Anda ingin menggunakannya secara eksklusif ;

 ketika Anda memiliki kelas yang menerapkan semua perilaku stack yang diperlukan,
Anda dapat menghasilkan tumpukan sebanyak yang Anda inginkan; Anda tidak perlu
menyalin atau mereplikasi bagian mana pun dari kode;
 kemampuan untuk memperkaya tumpukan dengan fungsi-fungsi baru berasal dari
warisan; Anda dapat membuat kelas baru (subkelas) yang mewarisi semua sifat yang
ada dari superclass, dan menambahkan beberapa yang baru.

Sekarang mari kita menulis implementasi stack baru dari awal. Kali ini, kami akan menggunakan
pendekatan objektif, membimbing Anda langkah demi langkah ke dunia pemrograman objek.

6.1.2.4 A short journey from procedural to object approach


Tumpukan - pendekatan objek
Tentu saja, gagasan utamanya tetap sama. Kami akan menggunakan daftar sebagai
penyimpanan tumpukan. Kita hanya perlu tahu bagaimana memasukkan daftar ke dalam kelas.
Mari kita mulai dari awal absolut - ini adalah bagaimana tumpukan tujuan dimulai:
class Stack:

Sekarang, kami mengharapkan dua hal dari itu:


 kita ingin kelas memiliki satu properti sebagai penyimpanan stack - kita harus "menginstal"
daftar di dalam setiap objek kelas(catatan: setiap objek harus memiliki daftar sendiri -
daftar tidak boleh dibagi di antara tumpukan yang berbeda)
 kemudian, kami ingin daftar tersebut disembunyikan dari pandangan pengguna kelas.

Bagaimana ini dilakukan?


Berbeda dengan bahasa pemrograman lain, Python tidak memiliki cara untuk memungkinkan
Anda mendeklarasikan properti seperti itu.

Sebagai gantinya, Anda perlu menambahkan pernyataan atau instruksi tertentu. Properti harus
ditambahkan ke kelas secara manual.

Bagaimana Anda menjamin bahwa kegiatan seperti itu terjadi setiap kali tumpukan baru dibuat?
Ada cara sederhana untuk melakukannya - Anda harus melengkapi kelas dengan fungsi
tertentu - kekhususannya ganda:
 itu harus diberi nama dengan cara yang ketat;
 itu dipanggil secara implisit, ketika objek baru dibuat.

Fungsi seperti ini disebut konstruktor , karena tujuan umumnya adalah untuk membangun objek
baru . Konstruktor harus tahu segalanya tentang struktur objek, dan harus melakukan semua
inisialisasi yang diperlukan.

Mari menambahkan konstruktor yang sangat sederhana ke kelas baru. Lihatlah cuplikannya:
class Stack: def __init__(self): print("Hi!") stackObject = Stack()

Dan sekarang:
 nama konstruktor selalu __init__;
 harus memiliki setidaknya satu parameter (kita akan membahas ini nanti); parameter
digunakan untuk mewakili objek yang baru dibuat - Anda dapat menggunakan
parameter untuk memanipulasi objek, dan untuk memperkaya dengan properti yang
dibutuhkan; Anda akan segera menggunakannya;
 catatan: parameter wajib biasanya dinamai self- itu hanya konvensi, tetapi Anda harus
mengikutinya - itu menyederhanakan proses membaca dan memahami kode Anda.

Kode ada di editor. Jalankan sekarang.


Berikut hasilnya:
Hi!

Catatan - tidak ada jejak memohon konstruktor di dalam kode. Itu telah dipanggil secara implisit
dan otomatis. Mari kita manfaatkan itu sekarang.

6.1.2.5 A short journey from procedural to object approach


Tumpukan - pendekatan objek: lanjutan
Setiap perubahan yang Anda lakukan di dalam konstruktor yang memodifikasi
status selfparameter akan mencerminkan objek yang baru dibuat.

Ini berarti Anda dapat menambahkan properti apa pun ke objek dan properti akan tetap di sana
sampai objek selesai masa pakainya atau properti secara eksplisit dihapus.
Sekarang mari kita tambahkan hanya satu properti ke objek baru - daftar untuk stack. Kami akan
beri nama stackList.

Sama seperti di sini:


class Stack: def __init__(self): self.stackList = [] stackObject = Stack()
print(len(stackObject.stackList))

catatan:
 kami telah menggunakan notasi bertitik , seperti saat menggunakan metode; ini adalah
konvensi umum untuk mengakses properti objek - Anda perlu memberi nama objek,
meletakkan titik ( .) setelahnya, dan menentukan nama properti yang diinginkan; jangan
gunakan tanda kurung! Anda tidak ingin menggunakan metode - Anda ingin mengakses
properti ;
 jika Anda menetapkan nilai properti untuk pertama kalinya (seperti pada konstruktor),
Anda membuatnya; sejak saat itu, objek telah mendapatkan properti dan siap untuk
menggunakan nilainya;
 kami telah melakukan sesuatu yang lebih dalam kode - kami telah mencoba
mengakses stackListproperti dari luar kelas segera setelah objek telah dibuat; kami ingin
memeriksa panjang tumpukan saat ini - apakah kami berhasil?
Ya, sudah - kode menghasilkan output berikut:
0

Ini bukan yang kita inginkan dari stack. Kami lebih memilih stackListuntuk disembunyikan dari
dunia luar . Apakah itu mungkin?
Ya, dan itu sederhana, tetapi tidak terlalu intuitif.

6.1.2.6 A short journey from procedural to object approach


Tumpukan - pendekatan objek: lanjutan
Lihatlah - kami telah menambahkan dua garis bawah sebelum stackListnama - tidak lebih:
class Stack: def __init__(self): self.__stackList = [] stackObject = Stack()
print(len(stackObject.__stackList))

Perubahan membatalkan program.


Mengapa?
Ketika setiap komponen kelas memiliki nama yang dimulai dengan dua garis bawah ( __), itu
menjadi pribadi - ini berarti bahwa itu hanya dapat diakses dari dalam kelas.
Anda tidak dapat melihatnya dari dunia luar. Ini adalah bagaimana Python
mengimplementasikan konsep enkapsulasi .

Jalankan program untuk menguji asumsi kami - pengecualian AttributeErrorharus dimunculkan.


6.1.2.7 A short journey from procedural to object approach
Pendekatan objek: tumpukan dari awal
Sekarang saatnya untuk dua fungsi (metode) yang menerapkan operasi pushdan pop . Python
mengasumsikan bahwa fungsi semacam ini (aktivitas kelas) harus dibenamkan di dalam tubuh
kelas - seperti konstruktor.

Kami ingin menjalankan fungsi pushdan popnilai ini. Ini berarti bahwa keduanya harus dapat
diakses oleh pengguna setiap kelas (berbeda dengan daftar yang dibuat sebelumnya, yang
disembunyikan dari pengguna kelas biasa).

Komponen seperti itu disebut publik , jadi Anda tidak dapat memulai namanya dengan dua
(atau lebih) garis bawah . Ada satu persyaratan lagi - nama harus memiliki tidak lebih dari satu
trailing garis bawah . Karena tidak ada jejak garis bawah yang sepenuhnya memenuhi
persyaratan, Anda dapat menganggap bahwa nama itu dapat diterima.

Fungsinya sendiri sederhana. Lihatlah:


class Stack: def __init__(self): self.__stackList = [] def push(self, val): self.__stackList.append(val)
def pop(self): val = self.__stackList[-1] del self.__stackList[-1] return val stackObject = Stack()
stackObject.push(3)
stackObject.push(2)
stackObject.push(1)
print(stackObject.pop())
print(stackObject.pop())
print(stackObject.pop())

Namun, ada sesuatu yang sangat aneh dalam kode tersebut. Fungsi-fungsinya terlihat familier,
tetapi mereka memiliki lebih banyak parameter daripada rekan-rekan proseduralnya.
Di sini, kedua fungsi memiliki parameter bernama selfdi posisi pertama dari daftar parameter.
Apakah itu dibutuhkan? Ya itu.

Semua metode harus memiliki parameter ini. Ini memainkan peran yang sama dengan
parameter konstruktor pertama.

Ini memungkinkan metode untuk mengakses entitas (properti dan aktivitas / metode) yang
dilakukan oleh objek yang sebenarnya . Anda tidak bisa menghilangkannya. Setiap kali Python
memanggil metode, ia secara implisit mengirimkan objek saat ini sebagai argumen pertama.
Ini berarti bahwa suatu metode wajib memiliki setidaknya satu parameter, yang digunakan oleh
Python sendiri - Anda tidak memiliki pengaruh terhadapnya.

Jika metode Anda tidak memerlukan parameter sama sekali, ini harus tetap ditentukan. Jika
dirancang untuk memproses hanya satu parameter, Anda harus menentukan dua, dan peran
yang pertama masih sama.

Ada satu hal lagi yang membutuhkan penjelasan - cara di mana metode dipanggil dari
dalam __stackListvariabel.
Untungnya, ini jauh lebih sederhana daripada tampilannya:
 tahap pertama mengirimkan objek secara keseluruhan → self;
 selanjutnya, Anda perlu masuk ke __stackListdaftar → self.__stackList;
 dengan __stackListsiap digunakan, Anda dapat melakukan langkah ketiga dan terakhir
→ self.__stackList.append(val).
Deklarasi kelas selesai, dan semua komponennya telah terdaftar. Kelas siap digunakan.

6.1.2.8 A short journey from procedural to object approach


Pendekatan objek: tumpukan dari awal
Memiliki kelas semacam itu membuka beberapa kemungkinan baru. Misalnya, Anda sekarang
dapat memiliki lebih dari satu tumpukan berperilaku dengan cara yang sama. Setiap tumpukan
akan memiliki salinan data pribadinya sendiri, tetapi akan menggunakan metode yang sama.
Inilah yang kita inginkan untuk contoh ini.
Analisis kode:
class Stack: def __init__(self): self.__stackList = [] def push(self, val): self.__stackList.append(val)
def pop(self): val = self.__stackList[-1] del self.__stackList[-1] return val stackObject1 = Stack()
stackObject2 = Stack() stackObject1.push(3) stackObject2.push(stackObject1.pop())
print(stackObject2.pop())

Ada dua tumpukan yang dibuat dari kelas dasar yang sama . Mereka bekerja secara
mandiri . Anda dapat membuat lebih banyak dari mereka jika Anda mau.
Jalankan kode di editor dan lihat apa yang terjadi. Lakukan eksperimen Anda sendiri.

6.1.2.9 A short journey from procedural to object approach


Pendekatan objek: tumpukan dari awal (lanjutan)
Analisis cuplikan di bawah ini - kami telah membuat tiga objek kelas Stack. Selanjutnya, kami
telah menyulap mereka. Cobalah untuk memprediksi nilai yang dihasilkan ke layar.

class Stack: def __init__(self): self.__stackList = [] def push(self, val): self.__stackList.append(val)


def pop(self): val = self.__stackList[-1] del self.__stackList[-1] return val littleStack = Stack()
anotherStack = Stack() funnyStack = Stack() littleStack.push(1)
anotherStack.push(littleStack.pop() + 1) funnyStack.push(anotherStack.pop() - 2)
print(funnyStack.pop())

Jadi, apa hasilnya? Jalankan program dan periksa apakah Anda benar.

6.1.2.10 A short journey from procedural to object approach


Pendekatan objek: tumpukan dari awal (lanjutan)
Sekarang mari kita melangkah lebih jauh. Mari kita tambahkan kelas baru untuk menangani
tumpukan .

Kelas baru harus dapat mengevaluasi jumlah semua elemen yang saat ini disimpan di stack .
Kami tidak ingin mengubah tumpukan yang ditentukan sebelumnya. Ini sudah cukup baik dalam
aplikasinya, dan kami tidak ingin itu diubah dengan cara apa pun. Kami ingin tumpukan baru
dengan kemampuan baru. Dengan kata lain, kami ingin membuat subkelas dari kelas yang
sudah ada Stack.

Langkah pertama mudah: cukup mendefinisikan subclass baru yang menunjuk ke kelas yang
akan digunakan sebagai superclass .
Seperti inilah tampilannya:
class AddingStack(Stack): pass

Kelas belum mendefinisikan komponen baru, tetapi itu tidak berarti bahwa itu kosong. Ia
mendapatkan semua komponen yang ditentukan oleh superclass - nama superclass ditulis
setelah titik dua langsung setelah nama kelas yang baru.
Inilah yang kami inginkan dari tumpukan baru:
 kami ingin pushmetode ini tidak hanya untuk mendorong nilai ke stack tetapi juga untuk
menambahkan nilai ke sumvariabel;
 kami ingin popfungsi tidak hanya mengeluarkan nilai dari tumpukan tetapi juga untuk
mengurangi nilai dari sumvariabel.

Pertama, mari kita tambahkan variabel baru ke kelas. Ini akan menjadi variabel pribadi , seperti
daftar tumpukan. Kami tidak ingin ada yang memanipulasi sumnilai.

Seperti yang sudah Anda ketahui, menambahkan properti baru ke kelas dilakukan oleh
konstruktor. Anda sudah tahu bagaimana melakukan itu, tetapi ada sesuatu yang sangat
menarik di dalam konstruktor. Lihatlah:
class AddingStack(Stack): def __init__(self): Stack.__init__(self) self.__sum = 0

Baris kedua tubuh konstruktor membuat properti bernama __sum- itu akan menyimpan total
semua nilai stack.

Tapi garis di depannya terlihat berbeda. Apa fungsinya? Apakah ini benar-benar perlu? Ya itu.
Berlawanan dengan banyak bahasa lain, Python memaksa Anda untuk secara eksplisit
memanggil konstruktor superclass . Menghilangkan titik ini akan memiliki efek berbahaya - objek
akan dicabut dari __stackListdaftar. Tumpukan seperti itu tidak akan berfungsi dengan baik.

Ini adalah satu-satunya waktu Anda dapat memanggil konstruktor yang tersedia secara eksplisit
- itu dapat dilakukan di dalam konstruktor superclass.
Perhatikan sintaks:
 Anda menentukan nama superclass (ini adalah kelas yang konstruktornya ingin Anda
jalankan)
 Anda menaruh titik ( .) setelahnya;
 Anda menentukan nama konstruktor;
 Anda harus menunjuk ke objek (instance kelas) yang harus diinisialisasi oleh konstruktor -
ini sebabnya Anda harus menentukan argumen dan menggunakan selfvariabel di
sini; Catatan: memohon metode apa pun (termasuk konstruktor) dari luar kelas tidak
pernah mengharuskan Anda untuk menempatkan selfargumen di daftar argumen-
memohon metode dari dalam kelas menuntut penggunaan eksplisit dari selfargumen,
dan itu harus diletakkan pertama pada daftar.

Catatan: umumnya merupakan praktik yang disarankan untuk memanggil konstruktor superclass
sebelum inisialisasi lain yang ingin Anda lakukan di dalam subkelas. Ini adalah aturan yang kami
ikuti dalam cuplikan.

6.1.2.11 A short journey from procedural to object approach


Pendekatan objek: tumpukan dari awal (lanjutan)
Kedua, mari kita tambahkan dua metode. Tetapi kami ingin bertanya: apakah ini benar-benar
menambah? Kami sudah memiliki metode ini di superclass. Bisakah kita melakukan sesuatu
seperti itu?

Ya kita bisa. Itu berarti bahwa kita akan mengubah fungsionalitas metode, bukan nama
mereka . Kita dapat mengatakan lebih tepat bahwa antarmuka (cara menangani objek) dari
kelas tetap sama ketika mengubah implementasi pada saat yang sama.
Mari kita mulai dengan implementasi pushfungsi. Inilah yang kami harapkan darinya:
 untuk menambahkan nilai ke __sumvariabel;
 untuk mendorong nilai ke tumpukan.

Catatan: aktivitas kedua sudah diterapkan di dalam superclass - jadi kita bisa
menggunakannya. Selanjutnya, kita harus menggunakannya, karena tidak ada cara lain untuk
mengakses __stackListvariabel.

Ini adalah bagaimana pushmetode terlihat di subclass:


def push(self, val): self.__sum += val Stack.push(self, val)

Perhatikan cara kami memanggil implementasi pushmetode sebelumnya (yang tersedia di


superclass):
 kita harus menentukan nama superclass; ini diperlukan untuk menunjukkan dengan jelas
kelas yang berisi metode, untuk menghindari kebingungan dengan fungsi lain dengan
nama yang sama;
 kita harus menentukan objek target dan meneruskannya sebagai argumen pertama (itu
tidak secara implisit ditambahkan ke doa dalam konteks ini.)
Kami mengatakan bahwa pushmetode ini telah diganti - nama yang sama seperti pada
superclass sekarang mewakili fungsi yang berbeda.

6.1.2.12 A short journey from procedural to object approach


Pendekatan objek: tumpukan dari awal (lanjutan)
Ini adalah popfungsi baru :
def pop(self): val = Stack.pop(self) self.__sum -= val return val

Sejauh ini, kami telah mendefinisikan __sumvariabel, tetapi kami belum menyediakan metode
untuk mendapatkan nilainya. Tampaknya disembunyikan. Bagaimana kita bisa
mengungkapkannya dan melakukannya dengan cara yang masih melindunginya dari
modifikasi?

Kita harus mendefinisikan metode baru. Kami akan beri nama getSum. Satu-satunya tugas
adalah mengembalikan __sumnilai .
Ini dia:
def getSum(self): return self.__sum

Jadi, mari kita lihat program di editor. Kode lengkap kelas ada di sana. Kami dapat memeriksa
fungsinya sekarang, dan kami melakukannya dengan bantuan beberapa baris kode tambahan.
Seperti yang Anda lihat, kami menambahkan lima nilai berikutnya ke tumpukan, mencetak
jumlahnya, dan mengambil semuanya dari tumpukan.

Oke, ini merupakan pengantar yang sangat singkat untuk pemrograman objek Python. Kami
akan segera memberi tahu Anda tentang hal itu secara lebih rinci.

6.1.3.1 OOP: Properties


Variabel instan
Secara umum, kelas dapat dilengkapi dengan dua jenis data yang berbeda untuk membentuk
properti kelas. Anda sudah melihat salah satu dari mereka ketika kami melihat tumpukan.

Jenis properti kelas ini ada ketika dan hanya ketika itu secara eksplisit dibuat dan ditambahkan
ke objek. Seperti yang sudah Anda ketahui, ini dapat dilakukan selama inisialisasi objek, dilakukan
oleh konstruktor.

Selain itu, dapat dilakukan kapan saja dalam kehidupan objek. Lebih lanjut, setiap properti yang
ada dapat dihapus kapan saja.
Pendekatan semacam itu memiliki beberapa konsekuensi penting:
 objek yang berbeda dari kelas yang sama dapat memiliki set properti yang berbeda ;
 harus ada cara untuk memeriksa dengan aman apakah objek tertentu memiliki
propertiyang ingin Anda manfaatkan (kecuali jika Anda ingin memprovokasi
pengecualian - itu selalu layak dipertimbangkan)
 setiap objek membawa set propertinya sendiri - mereka tidak saling mengganggu
dengan cara apa pun.

Variabel (properti) tersebut disebut variabel instan .


Kata instance menyarankan bahwa mereka terkait erat dengan objek (yang merupakan
instance kelas), bukan ke kelas itu sendiri. Mari kita lihat lebih dekat.

Berikut ini sebuah contoh:

class ExampleClass: def __init__(self, val = 1): self.first = val def setSecond(self, val): self.second =
val exampleObject1 = ExampleClass() exampleObject2 = ExampleClass(2)
exampleObject2.setSecond(3) exampleObject3 = ExampleClass(4) exampleObject3.third = 5
print(exampleObject1.__dict__) print(exampleObject2.__dict__) print(exampleObject3.__dict__)
Perlu satu penjelasan tambahan sebelum kita membahas lebih detail. Lihatlah tiga baris terakhir
dari kode.

Objek python, ketika dibuat, diberikan kumpulan kecil properti dan metode yang telah
ditentukan . Setiap objek telah mendapatkannya, apakah Anda menginginkannya atau
tidak. Salah satunya adalah variabel bernama __dict__(ini kamus).
Variabel berisi nama dan nilai dari semua properti (variabel) objek saat ini membawa. Mari kita
gunakan untuk menyajikan konten objek dengan aman.

Mari selami kode sekarang:


 kelas yang bernama ExampleClassmemiliki konstruktor, yang tanpa syarat menciptakan
variabel instan bernama first, dan menetapkannya dengan nilai yang melewati argumen
pertama (dari perspektif pengguna kelas) atau argumen kedua (dari perspektif
konstruktor); catat nilai default parameter - trik apa saja yang dapat Anda lakukan
dengan parameter fungsi reguler dapat diterapkan ke metode juga;
 kelas juga memiliki metode yang membuat variabel instance lain , bernama second;
 kami telah membuat tiga objek kelas ExampleClass, tetapi semua contoh ini berbeda:
o exampleObject1hanya memiliki nama properti first;
o exampleObject2memiliki dua sifat: firstdan second;
o exampleObject3telah diperkaya dengan properti bernama thirdon the fly, di luar
kode kelas - ini dimungkinkan dan sepenuhnya diizinkan.

Output program jelas menunjukkan bahwa asumsi kami benar - ini dia:
{'first': 1} {'second': 3, 'first': 2} {'third': 5, 'first': 4}

Ada satu kesimpulan tambahan yang harus dinyatakan di sini: memodifikasi variabel instan dari
objek apa pun tidak memiliki dampak pada semua objek yang tersisa . Variabel instan
sepenuhnya terisolasi satu sama lain.

6.1.3.2 OOP: Properties


Variabel instance: lanjutan
Lihatlah contoh yang dimodifikasi di editor.
Hampir sama dengan yang sebelumnya. Satu-satunya perbedaan adalah pada nama
properti. Kami telah menambahkan dua garis bawah ( __)di depannya.

Seperti yang Anda ketahui, penambahan semacam itu membuat variabel


instanmenjadi pribadi - menjadi tidak dapat diakses dari dunia luar.

Perilaku sebenarnya dari nama-nama ini sedikit lebih rumit, jadi mari kita jalankan programnya. Ini
adalah output:
{'_ExampleClass__first': 1} {'_ExampleClass__first': 2, '_ExampleClass__second': 3}
{'_ExampleClass__first': 4, '__third': 5}

Bisakah Anda melihat nama-nama aneh ini penuh dengan garis bawah? Dari mana mereka
berasal?

Ketika Python melihat bahwa Anda ingin menambahkan variabel instan ke objek dan Anda akan
melakukannya di dalam salah satu metode objek, itu mengacaukan operasi dengan cara
berikut:
 itu menempatkan nama kelas di depan nama Anda;
 itu menempatkan garis bawah tambahan di awal.
Inilah sebabnya mengapa __firstmenjadi _ExampleClass__first.

Nama tersebut sekarang dapat diakses sepenuhnya dari luar kelas . Anda dapat menjalankan
kode seperti ini:
print(exampleObject1._ExampleClass__first)

dan Anda akan mendapatkan hasil yang valid tanpa kesalahan atau pengecualian.
Seperti yang Anda lihat, menjadikan properti pribadi terbatas.
Mangling tidak akan berfungsi jika Anda menambahkan variabel instan di luar kode
kelas . Dalam hal ini, ia akan berperilaku seperti properti biasa lainnya.

6.1.3.3 OOP: Properties


Variabel kelas
Variabel kelas adalah properti yang hanya ada dalam satu salinan dan disimpan di luar objek
apa pun .

Catatan: tidak ada variabel instance jika tidak ada objek di kelas; variabel kelas ada dalam satu
salinan bahkan jika tidak ada objek di kelas.
Variabel kelas dibuat secara berbeda untuk saudara kandungnya. Contohnya akan memberi
tahu Anda lebih banyak:

class ExampleClass: counter = 0 def __init__(self, val = 1): self.__first = val ExampleClass.counter +=
1 exampleObject1 = ExampleClass() exampleObject2 = ExampleClass(2) exampleObject3 =
ExampleClass(4) print(exampleObject1.__dict__, exampleObject1.counter)
print(exampleObject2.__dict__, exampleObject2.counter) print(exampleObject3.__dict__,
exampleObject3.counter)

Melihat:
 ada tugas di daftar pertama definisi kelas - itu menetapkan variabel
bernama counterke 0 ; menginisialisasi variabel di dalam kelas tetapi di luar salah satu
metodenya menjadikan variabel sebagai variabel kelas;
 mengakses variabel seperti itu terlihat sama dengan mengakses atribut instance - Anda
dapat melihatnya di badan konstruktor; seperti yang Anda lihat, konstruktor menambah
variabel dengan satu; berlaku, variabel menghitung semua objek yang dibuat.

Menjalankan kode akan menyebabkan output berikut:


{'_ExampleClass__first': 1} 3 {'_ExampleClass__first': 2} 3 {'_ExampleClass__first': 4} 3

Dua kesimpulan penting datang dari contoh:


 variabel kelas tidak ditampilkan dalam objek__dict__ (ini wajar karena variabel kelas
bukan bagian dari objek) tetapi Anda selalu dapat mencoba melihat variabel dengan
nama yang sama, tetapi di tingkat kelas - kami akan menunjukkan kepada Anda ini
segera;
 variabel kelas selalu menyajikan nilai yang sama di semua instance kelas (objek)

6.1.3.4 OOP: Properties


Variabel kelas: lanjutan
Mengurai nama variabel kelas memiliki efek yang sama dengan yang sudah Anda kenal.
Lihatlah contoh di editor. Bisakah Anda menebak hasilnya?
Jalankan program dan periksa apakah prediksi Anda benar. Semuanya berfungsi seperti yang
diharapkan, bukan?

6.1.3.5 OOP: Properties


Variabel kelas: lanjutan
Kami katakan sebelumnya bahwa variabel kelas ada bahkan ketika tidak ada instance kelas
(objek) telah dibuat.

Sekarang kita akan mengambil kesempatan untuk menunjukkan kepada Anda perbedaan
antara dua __dict__variabel ini , satu dari kelas dan satu dari objek.
Lihatlah kode di editor. Buktinya ada di sana.
Mari kita lihat lebih dekat:
 kami mendefinisikan satu kelas bernama ExampleClass;
 kelas mendefinisikan satu variabel kelas bernama varia;
 konstruktor kelas menetapkan variabel dengan nilai parameter;
 memberi nama variabel adalah aspek yang paling penting dari contoh karena:
o mengubah tugas menjadi self.varia = valakan membuat variabel instan dengan
nama yang sama dengan yang ada di kelas;
o mengubah penugasan untuk varia = valakan beroperasi pada variabel lokal
metode; (kami sangat menganjurkan Anda untuk menguji kedua kasus di atas -
ini akan membuat Anda lebih mudah mengingat perbedaannya)
 baris pertama kode off-class mencetak nilai ExampleClass.variaatribut; note - kita
menggunakan nilai sebelum objek pertama dari kelas dipakai.

Jalankan kode di editor dan periksa hasilnya.


Seperti yang Anda lihat, kelas ' __dict__berisi lebih banyak data daripada rekan
objeknya. Sebagian besar dari mereka tidak berguna sekarang - yang kami ingin Anda periksa
dengan cermat menunjukkan nilai saat ini varia.

Perhatikan bahwa objek __dict__kosong - objek tidak memiliki variabel instan.

6.1.3.6 OOP: Properties


Memeriksa keberadaan atribut
Sikap Python terhadap objek objek menimbulkan satu masalah penting - berbeda dengan
bahasa pemrograman lain, Anda mungkin tidak berharap bahwa semua objek dari kelas yang
sama memiliki set properti yang sama .

Sama seperti pada contoh di editor. Lihatlah dengan cermat.


Objek yang dibuat oleh konstruktor hanya dapat memiliki satu dari dua atribut yang
mungkin: aatau b.

Menjalankan kode akan menghasilkan output berikut:


1 Traceback (most recent call last): File ".main.py", line 11, in print(exampleObject.b)
AttributeError: 'ExampleClass' object has no attribute 'b'

Seperti yang Anda lihat, mengakses atribut objek (kelas) yang tidak ada
menyebabkan pengecualian AttributeError .

6.1.3.7 OOP: Properties


Memeriksa keberadaan atribut: lanjutan
The try-kecuali instruksi memberi Anda kesempatan untuk menghindari masalah dengan sifat
tidak ada.

Sangat mudah - lihat kode di editor.

Seperti yang Anda lihat, tindakan ini tidak terlalu canggih. Intinya, kami baru saja menyapu
masalah di bawah karpet.
Untungnya, ada satu cara lagi untuk mengatasi masalah ini.

Python menyediakan fungsi yang dapat dengan aman memeriksa apakah ada objek / kelas
yang mengandung properti yang ditentukan . Fungsi ini dinamai hasattr, dan mengharapkan
dua argumen untuk diteruskan ke sana:
 kelas atau objek yang diperiksa;
 nama properti yang keberadaannya harus dilaporkan (catatan: harus berupa string yang
berisi nama atribut, bukan nama saja)

Fungsi mengembalikan Benar atau Salah.


Ini adalah bagaimana Anda dapat menggunakannya:
class ExampleClass: def __init__(self, val): if val % 2 != 0: self.a = 1 else: self.b = 1 exampleObject =
ExampleClass(1) print(exampleObject.a) if hasattr(exampleObject, 'b'): print(exampleObject.b)

6.1.3.8 OOP: Properties


Memeriksa keberadaan atribut: lanjutan
Jangan lupa bahwa hasattr()fungsinya dapat beroperasi di kelas juga. Anda dapat
menggunakannya untuk mengetahui apakah variabel kelas tersedia , seperti di sini dalam
contoh di editor.

Fungsi mengembalikan True jika kelas yang ditentukan berisi atribut yang diberikan,
dan False sebaliknya.
Bisakah Anda menebak output kode? Jalankan untuk memeriksa tebakan Anda.

Dan satu contoh lagi - lihat kode di bawah ini dan coba prediksi hasilnya:
class ExampleClass: a = 1 def __init__(self): self.b = 2 exampleObject = ExampleClass()
print(hasattr(exampleObject, 'b')) print(hasattr(exampleObject, 'a')) print(hasattr(ExampleClass,
'b')) print(hasattr(ExampleClass, 'a'))

Apakah kamu berhasil? Jalankan kode untuk memeriksa prediksi Anda.


Oke, kita sudah sampai di akhir bagian ini. Di bagian selanjutnya kita akan berbicara tentang
metode, karena metode menggerakkan objek dan membuatnya aktif.

6.1.4.1 OOP: Methods


Metode secara rinci
Mari meringkas semua fakta tentang penggunaan metode di kelas Python.
Untungnya, masalahnya lebih sederhana daripada membahas properti, karena tidak ada dua
jenis metode (baik instance maupun kelas) - hanya ada metode.

Seperti yang sudah Anda ketahui, metode adalah fungsi yang tertanam di dalam kelas .
Ada satu persyaratan mendasar - suatu metode wajib memiliki setidaknya satu parameter (tidak
ada yang namanya metode tanpa parameter - metode dapat dipanggil tanpa argumen, tetapi
tidak dideklarasikan tanpa parameter).

Parameter pertama (atau hanya) biasanya dinamai self. Kami menyarankan Anda mengikuti
konvensi - ini biasa digunakan, dan Anda akan menimbulkan beberapa kejutan dengan
menggunakan nama lain untuk itu.

Nama diri menyarankan tujuan parameter - itu mengidentifikasi objek yang metode ini
dipanggil .
Jika Anda akan memanggil metode, Anda tidak boleh melewatkan argumen
untuk selfparameter - Python akan mengaturnya untuk Anda.
Contoh di editor menunjukkan perbedaan.
Output kode:
method

Perhatikan cara kami membuat objek - kami telah memperlakukan nama kelas seperti fungsi ,
mengembalikan objek kelas yang baru dipakai.

Jika Anda ingin metode menerima parameter selain self, Anda harus:
 tempatkan mereka setelah selfdalam definisi metode;
 mengirimkannya selama doa tanpa menentukan self(seperti sebelumnya)
Sama seperti di sini:
class Classy: def method(self, par): print("method:", par) obj = Classy() obj.method(1)
obj.method(2) obj.method(3)

Output kode:
method: 1 method: 2 method: 3
6.1.4.2 OOP: Methods
Metode secara terperinci: lanjutan
The selfparameter digunakan untuk mendapatkan akses ke instance dan kelas objek variabel .
Contoh menunjukkan kedua cara memanfaatkan self:
class Classy: varia = 2 def method(self): print(self.varia, self.var) obj = Classy() obj.var = 3
obj.method()

Output kode:
23

The selfparameter juga digunakan untuk memanggil metode objek / kelas yang lain dari dalam
kelas .
Sama seperti di sini:
class Classy: def other(self): print("other") def method(self): print("method") self.other() obj =
Classy() obj.method()

Output kode:
method other

6.1.4.3 OOP: Methods


Metode secara terperinci: lanjutan
Jika Anda memberi nama metode seperti ini:, __init__itu tidak akan menjadi metode biasa - itu
akan menjadi konstruktor .

Jika kelas memiliki konstruktor, itu dipanggil secara otomatis dan implisit ketika objek kelas dipakai.
Konstruktor:
 adalah wajib untuk memiliki selfparameter (itu diatur secara otomatis, seperti biasa);
 mungkin (tetapi tidak perlu) memiliki lebih banyak parameterdari sekadar self; jika ini
terjadi, cara di mana nama kelas digunakan untuk membuat objek harus
mencerminkan __init__definisi;
 dapat digunakan untuk mengatur objek , yaitu, menginisialisasi keadaan internal dengan
benar, membuat variabel instance, instantiate objek lain jika keberadaannya diperlukan,
dll.

Lihatlah kode di editor. Contoh menunjukkan konstruktor yang sangat sederhana di tempat kerja.
Menjalankannya. Output kode:
object

Perhatikan bahwa konstruktor:


 tidak dapat mengembalikan nilai , karena ini dirancang untuk mengembalikan objek
yang baru dibuat dan tidak ada yang lain;
 tidak dapat dipanggil secara langsung baik dari objek atau dari dalam kelas (Anda
dapat memanggil konstruktor dari superclasses objek mana pun, tetapi kami akan
membahas masalah ini nanti.)

6.1.4.4 OOP: Methods


Metode secara terperinci: lanjutan
Seperti __init__metode, dan metode adalah fungsi, Anda dapat melakukan trik yang sama
dengan konstruktor / metode seperti yang Anda lakukan dengan fungsi biasa.

Contoh di editor menunjukkan cara mendefinisikan konstruktor dengan nilai argumen


default. Menguji.
Output kode:
object None
Semua yang kami katakan tentang susunan nama properti juga berlaku untuk nama metode -
metode yang namanya dimulai dengan __(sebagian) disembunyikan.

Contoh menunjukkan efek ini:


class Classy: def visible(self): print("visible") def __hidden(self): print("hidden") obj = Classy()
obj.visible() try: obj.__hidden() except: print("failed") obj._Classy__hidden()

Output kode:
visible failed hidden

Jalankan programnya, dan ujilah.

6.1.4.5 OOP: Methods


Kehidupan batin kelas dan objek
Setiap kelas Python dan setiap objek Python dilengkapi dengan seperangkat atribut berguna
yang dapat digunakan untuk memeriksa kemampuannya.
Anda sudah tahu salah satunya - ini adalah __dict__properti.
Mari kita amati bagaimana ini berkaitan dengan metode - lihat kode di editor.
Jalankan untuk melihat hasilnya. Periksa output dengan cermat.
Temukan semua metode dan atribut yang ditentukan. Temukan konteks di mana mereka ada:
di dalam objek atau di dalam kelas.

6.1.4.6 OOP: Methods


Kehidupan batin kelas dan objek: berlanjut
__dict__adalah kamus. Properti bawaan lain yang layak disebut adalah __name__, yang
merupakan string.
Properti berisi nama kelas . Tidak ada yang menarik, hanya seutas tali.
Catatan: __name__atribut tidak ada dari objek - hanya ada di dalam kelas .

Jika Anda ingin menemukan kelas objek tertentu , Anda bisa menggunakan fungsi
bernama type(), yang mampu (antara lain) untuk menemukan kelas yang telah digunakan
untuk instantiate objek apa pun.

Lihatlah kode di editor, jalankan, dan lihat sendiri.


Output kode:
Classy Classy

Catatan: pernyataan seperti ini:


print(obj.__name__) akan menyebabkan kesalahan.

6.1.4.7 OOP: Methods


Kehidupan batin kelas dan objek: berlanjut
__module__adalah string, juga - itu menyimpan nama modul yang berisi definisi kelas .
Mari kita periksa - jalankan kode di editor.
Output kode:
__main__ __main__

Seperti yang Anda ketahui, setiap modul yang dinamai __main__sebenarnya bukan modul,
tetapi file saat ini sedang dijalankan .

6.1.4.8 OOP: Methods


Kehidupan batin kelas dan objek: berlanjut
__bases__adalah sebuah tuple. The tuple berisi kelas (bukan nama kelas) yang superclasses
langsung untuk kelas.
Urutannya sama dengan yang digunakan di dalam definisi kelas.
Kami hanya akan menunjukkan kepada Anda contoh yang sangat mendasar, karena kami ingin
menyoroti cara kerja pewarisan .

Selain itu, kami akan menunjukkan kepada Anda bagaimana menggunakan atribut ini ketika
kami membahas aspek objektif pengecualian.

Catatan: hanya kelas yang memiliki atribut ini - objek tidak.


Kami telah mendefinisikan fungsi bernama printbases(), yang dirancang untuk menyajikan
konten tuple dengan jelas.

Lihatlah kode di editor. Analisis dan jalankan. Ini akan menampilkan:


( object ) ( object ) ( SuperOne SuperTwo )

Catatan: kelas tanpa superclasses eksplisit menunjuk ke objek (kelas Python yang telah
ditentukan) sebagai leluhur langsungnya.

6.1.4.9 OOP: Methods


Refleksi dan introspeksi
Semua cara ini memungkinkan programmer Python untuk melakukan dua kegiatan penting
khusus untuk banyak bahasa yang objektif. Mereka:
 introspeksi , yang merupakan kemampuan suatu program untuk memeriksa jenis atau
properti suatu objek pada saat runtime;
 refleksi , yang melangkah lebih jauh, dan merupakan kemampuan program untuk
memanipulasi nilai, properti, dan / atau fungsi suatu objek saat runtime.

Dengan kata lain, Anda tidak perlu mengetahui definisi kelas / objek yang lengkap untuk
memanipulasi objek, karena objek dan / atau kelasnya mengandung metadata yang
memungkinkan Anda mengenali fitur-fiturnya selama eksekusi program.

6.1.4.10 OOP: Methods


Kelas investigasi
Apa yang bisa Anda ketahui tentang kelas di Python? Jawabannya sederhana - semuanya.
Refleksi dan introspeksi memungkinkan seorang programmer untuk melakukan apa saja dengan
setiap objek, tidak peduli dari mana asalnya.
Analisis kode dalam editor.

Fungsi bernama incIntsI()mendapat objek dari kelas apa pun, memindai isinya untuk
menemukan semua atribut integer dengan nama yang dimulai dengan i , dan
menambahkannya dengan satu.
Mustahil? Tidak semuanya!

Begini Cara kerjanya:


 baris 1: tentukan kelas yang sangat sederhana ...
 baris 3 hingga 10: ... dan isi dengan beberapa atribut;
 baris 12: ini adalah fungsi kami!
 baris 13: memindai __dict__atribut, mencari semua nama atribut;
 baris 14: jika nama dimulai dengan i...
 baris 15: ... gunakan getattr()fungsi untuk mendapatkan nilainya saat
ini; catatan: getattr()mengambil dua argumen: objek, dan nama propertinya (sebagai
string), dan mengembalikan nilai atribut saat ini;
 baris 16: periksa apakah nilainya bertipe integer, dan gunakan fungsi isinstance()untuk
tujuan ini (kita akan membahas ini nanti);
 baris 17: jika cek berjalan baik, naikkan nilai properti dengan
memanfaatkan setattr()fungsi; fungsi mengambil tiga argumen: objek, nama properti
(sebagai string), dan nilai baru properti.
Output kode:
{'a': 1, 'integer': 4, 'b': 2, 'i': 3, 'z': 5, 'ireal': 3.5} {'a': 1, 'integer': 5, 'b': 2, 'i': 4, 'z': 5, 'ireal': 3.5}

Itu saja!

6.1.5.1 OOP Fundamentals: Inheritance


Warisan - mengapa dan bagaimana?
Sebelum kita mulai berbicara tentang warisan, kami ingin menyajikan mekanisme baru dan
praktis yang digunakan oleh kelas dan objek Python - ini adalah cara di mana objek dapat
memperkenalkan dirinya .

Mari kita mulai dengan sebuah contoh. Lihatlah kode di editor.


Program mencetak hanya satu baris teks, yang dalam kasus kami adalah ini:
<__main__.Star object at 0x7f1074cc7c50>

Jika Anda menjalankan kode yang sama di komputer Anda, Anda akan melihat sesuatu yang
sangat mirip, meskipun angka heksadesimal (substring dimulai dengan 0x ) akan berbeda,
karena itu hanya pengenal objek internal yang digunakan oleh Python, dan tidak mungkin
bahwa itu akan muncul sama ketika kode yang sama dijalankan di lingkungan yang berbeda.

Seperti yang Anda lihat, hasil cetak di sini tidak benar-benar berguna, dan sesuatu yang lebih
spesifik, atau lebih cantik, mungkin lebih disukai.
Untungnya, Python menawarkan fungsi seperti itu.

6.1.5.2 OOP Fundamentals: Inheritance


Warisan - mengapa dan bagaimana?
Ketika Python membutuhkan kelas / objek apa pun untuk disajikan sebagai string (menempatkan
objek sebagai argumen dalam print()fungsi doa cocok dengan kondisi ini) ia mencoba untuk
memanggil metode yang bernama __str__()dari objek dan menggunakan string yang
dikembalikan.

__str__()Metode default mengembalikan string sebelumnya - jelek dan tidak terlalu


informatif. Anda dapat mengubahnya hanya dengan mendefinisikan metode nama Anda
sendiri .

Kami baru saja melakukannya - lihat kode di editor.


__str__()Metode baru ini membuat string yang terdiri dari nama bintang dan galaksi - tidak ada
yang istimewa, tetapi hasil cetak terlihat lebih baik sekarang, bukan?
Bisakah Anda menebak hasilnya? Jalankan kode untuk memeriksa apakah Anda benar.

6.1.5.3 OOP Fundamentals: Inheritance


Warisan - mengapa dan bagaimana?
Warisan istilah lebih tua dari pemrograman komputer, dan itu menggambarkan praktik umum
melewati barang yang berbeda dari satu orang ke orang lain setelah kematian orang itu. Istilah
itu, ketika terkait dengan pemrograman komputer, memiliki arti yang sama sekali berbeda.

Mari kita tentukan istilah untuk tujuan kita:


Warisan adalah praktik umum (dalam pemrograman objek) yang meneruskan atribut dan
metode dari superclass (didefinisikan dan ada) ke kelas yang baru dibuat, yang disebut
subclass .
Dengan kata lain, pewarisan adalah cara membangun kelas baru, bukan dari awal, tetapi
dengan menggunakan repertoar sifat yang sudah didefinisikan . Kelas baru mewarisi (dan ini
kuncinya) semua peralatan yang sudah ada, tetapi dapat menambahkan beberapa yang baru
jika diperlukan.
Berkat itu, dimungkinkan untuk membangun kelas yang lebih khusus (lebih
konkret)menggunakan beberapa set aturan dan perilaku umum yang telah ditetapkan.

The most important factor of the process is the relation between the superclass and all of its
subclasses (note: if B is a subclass of A and C is a subclass of B, this also means than C is a subclass
of A, as the relationship is fully transitive).
A very simple example of two-level inheritance is presented here:
class Vehicle: pass class LandVehicle(Vehicle): pass class TrackedVehicle(LandVehicle): pass

All the presented classes are empty for now, as we're going to show you how the mutual relations
between the super- and subclasses work. We'll fill them with contents soon.
We can say that:
 The Vehicle class is the superclass for both the LandVehicle and TrackedVehicleclasses;
 The LandVehiclekelas adalah subclass dari Vehicledan superclass
dari TrackedVehiclepada waktu yang sama;
 The TrackedVehiclekelas adalah subclass dari kedua Vehicledan LandVehiclekelas.

Pengetahuan di atas berasal dari membaca kode (dengan kata lain, kita tahu karena kita bisa
melihatnya).
Apakah Python mengetahui hal yang sama? Apakah mungkin untuk bertanya kepada Python
tentang hal itu? Ya itu.

6.1.5.4 OOP Fundamentals: Inheritance


Pewarisan: issubclass ()
Python menawarkan fungsi yang mampu mengidentifikasi hubungan antara dua kelas , dan
meskipun diagnosisnya tidak kompleks, ia dapat memeriksa apakah kelas tertentu adalah
subkelas dari kelas lain .
Begini tampilannya:
issubclass(ClassOne, ClassTwo)

Fungsi mengembalikan True jika ClassOnemerupakan subclass dari ClassTwo,


dan False sebaliknya.

Mari kita lihat dalam aksi - ini mungkin akan mengejutkan Anda. Lihatlah kode di editor. Baca
dengan cermat.

Ada dua loop bersarang. Tujuannya adalah untuk memeriksa semua pasangan kelas yang
mungkin dipesan, dan untuk mencetak hasil pemeriksaan untuk menentukan apakah pasangan
cocok dengan hubungan subkelas-superclass .
Jalankan kodenya. Program menghasilkan output sebagai berikut:
True False False True True False True True True

Mari kita buat hasilnya lebih mudah dibaca:


↓ adalah subkelas dari → Kendaraan Kendaraan Land Kendaraan dilacak
Kendaraan Benar Salah Salah
Kendaraan Land Benar Benar Salah
Kendaraan dilacak Benar Benar Benar
Ada satu pengamatan penting yang harus dilakukan: setiap kelas dianggap sebagai subkelas
dari dirinya sendiri .
6.1.5.5 OOP Fundamentals: Inheritance
Pewarisan: isinstance ()
Seperti yang sudah Anda ketahui, sebuah objek adalah inkarnasi dari sebuah kelas . Ini berarti
bahwa objek itu seperti kue yang dipanggang menggunakan resep yang termasuk dalam kelas.
Ini dapat menghasilkan beberapa masalah penting.

Mari kita asumsikan bahwa Anda memiliki kue (misalnya, ketika argumen dilewatkan ke fungsi
Anda). Anda ingin tahu resep apa yang telah digunakan untuk
membuatnya. Mengapa? Karena Anda ingin tahu apa yang diharapkan dari itu, misalnya,
apakah mengandung kacang atau tidak, yang merupakan informasi penting bagi sebagian
orang.

Demikian pula, ini dapat menjadi sangat penting jika objek memiliki (atau tidak memiliki)
karakteristik tertentu. Dengan kata lain, apakah itu objek kelas tertentu atau tidak .
Fakta seperti itu dapat dideteksi oleh fungsi bernama isinstance():

isinstance(objectName, ClassName)

Fungsi mengembalikan True jika objek adalah turunan dari kelas, atau Falsesebaliknya.
Menjadi turunan dari sebuah kelas berarti bahwa objek (kue) telah disiapkan menggunakan
resep yang terkandung dalam kelas atau salah satu dari superclasses-nya .

Jangan lupa: jika sebuah subclass mengandung setidaknya peralatan yang sama dengan
superclasses-nya, itu berarti bahwa objek-objek dari subclass dapat melakukan hal yang sama
seperti objek-objek yang berasal dari superclass, ergo, ini adalah turunan dari kelas rumahnya
dan kacamata supernya.
Ayo kita coba. Analisis kode dalam editor.

Kami telah membuat tiga objek, satu untuk setiap kelas. Selanjutnya, menggunakan dua loop
bersarang, kami memeriksa semua pasangan kelas objek yang mungkin untuk mengetahui
apakah objek adalah instance dari kelas .
Jalankan kodenya.
Inilah yang kami dapatkan:
True False False True True False True True True

Mari buat hasilnya lebih mudah dibaca sekali lagi:


↓ adalah turunan dari → Kendaraan Kendaraan Land Kendaraan dilacak
kendaraan saya Benar Salah Salah
myLandVehicle Benar Benar Salah
myTrackedVehicle Benar Benar Benar
Apakah tabel mengkonfirmasi harapan kita?

6.1.5.6 OOP Fundamentals: Inheritance


Warisan: operator is
Ada juga operator Python yang layak disebut, karena merujuk langsung ke objek - ini dia:
objectOne is objectTwo

The iscek Operator apakah dua variabel ( objectOnedan objectTwodi sini) mengacu pada
objek yang sama .
Jangan lupa bahwa variabel tidak menyimpan objek itu sendiri, tetapi hanya pegangan yang
menunjuk ke memori Python internal .
Menetapkan nilai variabel objek ke variabel lain tidak menyalin objek, tetapi hanya
menangani. Inilah sebabnya mengapa operator seperti ismungkin sangat berguna dalam
keadaan tertentu.
Lihatlah kode di editor. Mari kita analisa:
 ada kelas yang sangat sederhana yang dilengkapi dengan konstruktor sederhana, hanya
menciptakan satu properti. Kelas digunakan untuk instantiate dua objek. Yang pertama
kemudian ditugaskan ke variabel lain, dan valpropertinya bertambah satu.
 setelah itu, isoperator diterapkan tiga kali untuk memeriksa semua pasangan objek yang
mungkin, dan semua valnilai properti juga dicetak.
 bagian terakhir dari kode melakukan percobaan lain. Setelah tiga penugasan, kedua
string berisi teks yang sama, tetapi teks-teks ini disimpan dalam objek yang berbeda .
Kode dicetak:
False False True 1 2 1 True False

Hasilnya membuktikan bahwa ob1dan ob3sebenarnya benda yang sama,


sementara str1dan str2tidak, meskipun isinya sama.

6.1.5.7 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode
Sekarang kita akan melihat bagaimana Python berurusan dengan metode pewarisan.
Lihatlah contoh di editor. Mari kita analisa:
 ada kelas bernama Super, yang mendefinisikan konstruktornya sendiri yang digunakan
untuk menetapkan properti objek, bernama name.
 kelas mendefinisikan __str__()metode juga, yang membuat kelas dapat menyajikan
identitasnya dalam bentuk teks yang jelas.
 kelas selanjutnya digunakan sebagai basis untuk membuat subclass
bernama Sub. The Subkelas mendefinisikan konstruktor sendiri, yang memanggil satu
supernya. Perhatikan bagaimana kita sudah melakukannya: Super.__init__(self, name).
 kami secara eksplisit menamai superclass, dan menunjuk ke metode untuk
memanggil __init__(), memberikan semua argumen yang diperlukan.
 kami telah instantiated satu objek kelas Subdan mencetaknya.

Output kode:
My name is Andy.

Catatan: Karena tidak ada __str__()metode di dalam Subkelas, string yang dicetak akan
diproduksi di dalam Superkelas. Ini berarti bahwa __str__()metode ini telah diwarisi oleh Subkelas.

6.1.5.8 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Lihatlah kode di editor. Kami telah memodifikasinya untuk menunjukkan kepada Anda metode
lain untuk mengakses entitas apa pun yang ditentukan di dalam superclass.

Dalam contoh terakhir, kami secara eksplisit menamai superclass. Dalam contoh ini, kami
menggunakan super()fungsi, yang mengakses superclass tanpa perlu tahu namanya :
super().__init__(name)

The super()Fungsi menciptakan konteks di mana Anda tidak perlu (apalagi, Anda harus tidak)
lulus argumen diri dengan metode yang dipanggil - ini adalah mengapa hal itu mungkin untuk
mengaktifkan konstruktor superclass hanya menggunakan satu argumen.

Catatan: Anda dapat menggunakan mekanisme ini tidak hanya untuk memanggil konstruktor
superclass, tetapi juga untuk mendapatkan akses ke sumber daya yang tersedia di dalam
superclass .

6.1.5.9 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Mari kita coba melakukan sesuatu yang serupa, tetapi dengan properti (lebih tepatnya:
dengan variabel kelas ).
Lihatlah contoh di editor.

Seperti yang Anda lihat, Superkelas mendefinisikan satu variabel kelas bernama supVar,
dan Subkelas mendefinisikan variabel bernama subVar.
Kedua variabel ini terlihat di dalam objek kelas Sub- inilah sebabnya kode menghasilkan:
21

6.1.5.10 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Efek yang sama dapat diamati dengan variabel instan - lihat contoh kedua di editor.
The Subkonstruktor kelas menciptakan sebuah variabel contoh bernama subVar,
sedangkan Superkonstruktor melakukan hal yang sama dengan variabel
bernama supVar. Seperti sebelumnya, kedua variabel dapat diakses dari dalam objek kelas Sub.
Output program adalah:
12 11

Catatan: keberadaan supVarvariabel jelas dikondisikan oleh Superpermohonan konstruktor


kelas. Mengabaikannya akan mengakibatkan tidak adanya variabel dalam objek yang dibuat
(coba sendiri).

6.1.5.11 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Sekarang mungkin untuk merumuskan pernyataan umum yang menggambarkan perilaku
Python.

Saat Anda mencoba mengakses entitas objek apa pun, Python akan mencoba (dalam urutan
ini):
 menemukannya di dalam objek itu sendiri;
 temukan di semua kelas yangterlibat dalam garis warisan objek dari bawah ke atas;
Jika kedua hal di atas gagal, eksepsi ( AttributeError) dinaikkan .

Kondisi pertama mungkin perlu perhatian tambahan. Seperti yang Anda ketahui, semua objek
yang berasal dari kelas tertentu mungkin memiliki set atribut yang berbeda, dan beberapa
atribut dapat ditambahkan ke objek lama setelah penciptaan objek.

Contoh dalam editor merangkum ini dalam garis pewarisan tiga tingkat . Analisis dengan
cermat.

Semua komentar yang kami buat sejauh ini terkait dengan pewarisan tunggal , ketika subkelas
memiliki tepat satu superclass. Ini adalah situasi yang paling umum (dan yang direkomendasikan
juga).

Python, bagaimanapun, menawarkan lebih banyak di sini. Dalam pelajaran selanjutnya kami
akan menunjukkan beberapa contoh pewarisan berganda .

6.1.5.12 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Warisan berganda terjadi ketika suatu kelas memiliki lebih dari satu superclass .
Secara sintaksis, pewarisan tersebut disajikan sebagai daftar superclasses yang dipisahkan koma
yang dimasukkan ke dalam tanda kurung setelah nama kelas baru - seperti di sini:
class SuperA: varA = 10 def funA(self): return 11 class SuperB: varB = 20 def funB(self): return 21
class Sub(SuperA, SuperB): pass obj = Sub() print(obj.varA, obj.funA()) print(obj.varB, obj.funB())

The Subkelas memiliki dua superclasses: SuperAdan SuperB. Ini berarti bahwa Subkelas mewarisi
semua barang yang ditawarkan oleh keduanya SuperAdanSuperB .
Kode dicetak:
10 11 20 21

Sekarang saatnya untuk memperkenalkan istilah baru - penggantian .


Menurut Anda apa yang akan terjadi jika lebih dari satu superclasses mendefinisikan entitas
dengan nama tertentu?

6.1.5.13 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Mari kita menganalisis contoh di editor.
Baik, Level1dan Level2kelas mendefinisikan metode bernama fun()dan properti
bernama var. Apakah ini berarti bahwa Level3objek kelas akan dapat mengakses dua salinan
dari setiap entitas? Tidak semuanya.

Entitas yang didefinisikan kemudian (dalam pengertian pewarisan) menimpa entitas yang sama
yang didefinisikan sebelumnya . Inilah sebabnya mengapa kode menghasilkan output berikut:
200 201

Seperti yang Anda lihat, varvariabel kelas dan fun()metode dari Level2kelas menimpa entitas
dari nama yang sama yang berasal dari Level1kelas.

Fitur ini dapat dengan sengaja digunakan untuk memodifikasi perilaku kelas default (atau yang
didefinisikan sebelumnya) ketika salah satu kelasnya perlu bertindak dengan cara yang berbeda
dengan leluhurnya.

Kita juga dapat mengatakan bahwa Python mencari entitas dari bawah ke atas , dan
sepenuhnya puas dengan entitas pertama dari nama yang diinginkan.

Bagaimana cara kerjanya ketika sebuah kelas memiliki dua leluhur yang menawarkan entitas
yang sama, dan mereka berada pada level yang sama? Dengan kata lain, apa yang harus
Anda harapkan ketika sebuah kelas muncul menggunakan multiple inheritance? Mari kita lihat
ini.

6.1.5.14 OOP Fundamentals: Inheritance


Bagaimana Python menemukan properti dan metode: lanjutan
Mari kita lihat contoh di editor.
The Subkelas mewarisi barang dari dua superclasses, Leftdan Right(nama-nama ini
dimaksudkan untuk menjadi bermakna).

Tidak ada keraguan bahwa variabel kelas varRightberasal dari Rightkelas, dan varLeftberasal
dari Leftmasing-masing.

Ini jelas. Tapi dari mana datangnya var? Apakah mungkin untuk menebaknya? Masalah yang
sama ditemui dengan fun()metode - apakah akan dipanggil dari Leftatau dari Right? Mari kita
jalankan program - outputnya adalah:
L LL RR Left

Ini membuktikan bahwa kedua kasus yang tidak jelas memiliki solusi di dalam Leftkelas. Apakah
ini premis yang cukup untuk merumuskan aturan umum? Ya itu.

Kita dapat mengatakan bahwa Python mencari komponen objek dalam urutan berikut:
 di dalam objek itu sendiri;
 dalam kacamata supernya , dari bawah ke atas;
 jika ada lebih dari satu kelas pada jalur pewarisan tertentu, Python memindai mereka dari
kiri ke kanan.
Apakah Anda membutuhkan sesuatu yang lebih? Buat sedikit amandemen dalam kode -
ganti: class Sub(Left, Right):dengan class Sub(Right, Left)::, lalu jalankan kembali program, dan
lihat apa yang terjadi.
Apa yang kamu lihat sekarang? Kami melihat:
R LL RR Right

Apakah Anda melihat hal yang sama, atau sesuatu yang berbeda?

6.1.5.15 OOP Fundamentals: Inheritance


Cara membangun hierarki kelas
Membangun hierarki kelas bukan hanya seni demi seni.
Jika Anda membagi masalah di antara kelas-kelas dan memutuskan mana yang harus
ditempatkan di bagian atas dan yang harus ditempatkan di bagian bawah hierarki, Anda harus
menganalisis masalah dengan hati-hati, tetapi sebelum kami menunjukkan kepada Anda
bagaimana melakukannya (dan bagaimana tidak melakukannya), kami ingin menyoroti efek
yang menarik. Ini bukan sesuatu yang luar biasa (itu hanya konsekuensi dari aturan umum yang
disajikan sebelumnya), tetapi mengingat itu mungkin menjadi kunci untuk memahami
bagaimana beberapa kode bekerja, dan bagaimana efeknya dapat digunakan untuk
membangun serangkaian kelas yang fleksibel.

Lihatlah kode di editor. Mari kita analisa:


 ada dua kelas, bernama Onedan Two, saat Twoditurunkan dari One. Tidak ada yang
spesial. Namun, satu hal yang terlihat luar biasa - doit()metodenya.
 yang doit()metode ini didefinisikan dua kali : awalnya dalam Onedan kemudian di
dalam Two. Esensi dari contoh terletak pada kenyataan bahwa itu dipanggil hanya
sekali - di dalam One.

Pertanyaannya adalah - manakah dari dua metode yang akan dipanggil oleh dua baris terakhir
dari kode?

Doa pertama tampaknya sederhana, dan sederhana, sebenarnya -


memanggil doanything()dari objek bernama onejelas akan mengaktifkan metode pertama.

Doa kedua membutuhkan perhatian. Ini juga sederhana, jika Anda ingat bagaimana Python
menemukan komponen kelas. Doa kedua akan diluncurkan doit()dalam bentuk yang ada di
dalam Twokelas, terlepas dari fakta bahwa doa itu terjadi di dalam Onekelas.

Akibatnya, kode menghasilkan output berikut:


doit from One doit from Two

Catatan: situasi di mana subclass dapat memodifikasi perilaku superclass-nya (seperti dalam
contoh) disebut polimorfisme . Kata ini berasal dari bahasa Yunani (polis: "banyak, banyak" dan
morphe, "bentuk, bentuk"), yang berarti bahwa satu dan kelas yang sama dapat mengambil
berbagai bentuk tergantung pada definisi ulang yang dilakukan oleh salah satu subkelasnya.
Metode, didefinisikan ulang di salah satu superclasses, sehingga mengubah perilaku superclass,
disebut virtual .

Dengan kata lain, tidak ada kelas yang diberikan sekali dan untuk semua. Perilaku setiap kelas
dapat dimodifikasi setiap saat oleh subkelasnya.
Kami akan menunjukkan kepada Anda bagaimana menggunakan polimorfisme untuk
memperluas fleksibilitas kelas .

6.1.5.16 OOP Fundamentals: Inheritance


Cara membangun hierarki kelas: lanjutan
Lihatlah contoh di editor.
Apakah itu menyerupai sesuatu? Ya tentu saja. Ini merujuk pada contoh yang ditunjukkan di
awal modul ketika kita berbicara tentang konsep umum pemrograman objektif.
Ini mungkin terlihat aneh, tetapi kami tidak menggunakan warisan dengan cara apa pun - hanya
untuk menunjukkan kepada Anda bahwa itu tidak membatasi kami, dan kami berhasil
mendapatkan warisan kami.

Kami mendefinisikan dua kelas terpisah yang dapat menghasilkan dua jenis kendaraan
darat. Perbedaan utama di antara mereka adalah bagaimana mereka berubah. Kendaraan
roda hanya memutar roda depan (umumnya). Kendaraan yang dilacak harus menghentikan
salah satu lintasan.

Bisakah kamu mengikuti kodenya?


 kendaraan yang dilacak melakukan belokan dengan berhenti dan bergerak di salah satu
relnya (ini dilakukan dengan controltrack()metode ini, yang akan diimplementasikan
kemudian)
 kendaraan roda membelok ketika roda depannya berputar (ini dilakukan
dengan turnfrontwheels()metode)
 yang turn()metode menggunakan metode yang cocok untuk setiap kendaraan tertentu.

Bisakah Anda melihat apa yang salah dengan kode tersebut ?


The turn()metode terlihat terlalu mirip dengan meninggalkan mereka dalam bentuk ini.

Mari kita membangun kembali kode - kita akan memperkenalkan superclass untuk
mengumpulkan semua aspek yang sama dari kendaraan mengemudi, memindahkan semua
spesifik ke subclass.

6.1.5.17 OOP Fundamentals: Inheritance


Cara membangun hierarki kelas: lanjutan
Lihatlah kode di editor lagi. Inilah yang kami lakukan:
 kami mendefinisikan superclass bernama Vehicle, yang menggunakan turn()metode ini
untuk mengimplementasikan skema umum belokan, sedangkan belokan itu sendiri
dilakukan dengan metode bernama changedirection(); catatan: metode sebelumnya
kosong, karena kita akan memasukkan semua detail ke dalam subkelas (metode seperti
itu sering disebut metode abstrak , karena hanya menunjukkan beberapa kemungkinan
yang akan dipakai kemudian)
 kami mendefinisikan sebuah subclass bernama TrackedVehicle(catatan: itu berasal
dari Vehiclekelas) yang dipakai changedirection()metode dengan menggunakan
metode spesifik (beton) bernamacontroltrack()
 masing-masing, subclass yang dinamai WheeledVehiclemelakukan trik yang sama,
tetapi menggunakan turnfrontwheel()metode untuk memaksa kendaraan berbelok.

Keuntungan yang paling penting (menghilangkan masalah keterbacaan) adalah bentuk kode
ini memungkinkan Anda untuk mengimplementasikan algoritma putaran baru hanya dengan
memodifikasi turn()metode, yang dapat dilakukan hanya di satu tempat, karena semua
kendaraan akan mematuhinya.

Beginilah polimorfisme membantu pengembang menjaga kode tetap bersih dan konsisten .

6.1.5.18 OOP Fundamentals: Inheritance


Cara membangun hierarki kelas: lanjutan
Warisan bukan satu-satunya cara membangun kelas yang bisa beradaptasi. Anda dapat
mencapai tujuan yang sama (tidak selalu, tetapi sangat sering) dengan menggunakan teknik
bernama komposisi.
Komposisi adalah proses menyusun objek menggunakan objek berbeda lainnya . Objek yang
digunakan dalam komposisi menghasilkan seperangkat sifat yang diinginkan (properti dan / atau
metode) sehingga kita dapat mengatakan bahwa mereka bertindak seperti balok yang
digunakan untuk membangun struktur yang lebih rumit.
Bisa dibilang:
 warisan memperluas kemampuan kelas dengan menambahkan komponen baru dan
memodifikasi yang sudah ada; dengan kata lain, resep lengkap terkandung di dalam
kelas itu sendiri dan semua leluhurnya; objek mengambil semua barang milik kelas dan
memanfaatkannya;
 komposisi memproyeksikan kelas sebagai wadah yang dapat menyimpan dan
menggunakan objek lain (berasal dari kelas lain) di mana masing-masing objek
mengimplementasikan bagian dari perilaku kelas yang diinginkan.

Mari kita ilustrasikan perbedaannya dengan menggunakan kendaraan yang didefinisikan


sebelumnya. Pendekatan sebelumnya membawa kami ke hierarki kelas di mana kelas paling
atas menyadari aturan umum yang digunakan dalam memutar kendaraan, tetapi tidak tahu
bagaimana mengontrol komponen yang sesuai (roda atau trek).

Subclass menerapkan kemampuan ini dengan memperkenalkan mekanisme khusus. Mari kita
lakukan (hampir) hal yang sama, tetapi menggunakan komposisi. Kelas - seperti dalam contoh
sebelumnya - menyadari cara membelokkan kendaraan, tetapi pergantian aktual dilakukan
oleh objek khusus yang disimpan di properti yang bernama controller. The controllermampu
mengendalikan kendaraan dengan memanipulasi bagian-bagian kendaraan yang
bersangkutan.

Lihatlah ke editor - ini adalah bagaimana itu bisa terlihat.

Ada dua kelas yang dinamai Tracksdan Wheels- mereka tahu cara mengendalikan arah
kendaraan. Ada juga kelas bernama Vehicleyang dapat menggunakan salah satu dari
pengontrol yang tersedia (dua sudah didefinisikan, atau lainnya didefinisikan di masa depan) -
kelas controlleritu sendiri diteruskan ke kelas selama inisialisasi.

Dengan cara ini, kemampuan kendaraan untuk berputar disusun menggunakan objek eksternal,
tidak diimplementasikan di dalam Vehiclekelas.
Dengan kata lain, kami memiliki kendaraan universal dan dapat memasang trek atau roda di
atasnya.

Kode menghasilkan output berikut:


wheels: True True wheels: True False tracks: False True tracks: False False

6.1.5.19 OOP Fundamentals: Inheritance


Warisan tunggal vs. banyak warisan
Seperti yang sudah Anda ketahui, tidak ada hambatan untuk menggunakan multiple inheritance
di Python. Anda dapat memperoleh kelas baru apa saja dari lebih dari satu kelas yang telah
ditentukan sebelumnya.

Hanya ada satu "tapi". Fakta bahwa Anda dapat melakukannya bukan berarti Anda harus
melakukannya.

Jangan lupakan itu:


 satu kelas warisan selalu lebih sederhana, lebih aman, dan lebih mudah dipahami dan
dipelihara;
 multiple inheritance selalu berisiko, karena Anda memiliki lebih banyak peluang untuk
melakukan kesalahan dalam mengidentifikasi bagian-bagian dari kacamata super yang
secara efektif akan mempengaruhi kelas baru;

 pewarisan berganda mungkin membuat pengesampingan menjadi sangat rumit; selain


itu, menggunakan super()fungsi menjadi ambigu;
 multiple inheritance melanggar prinsip tanggung jawab tunggal (lebih detail di
sini: https://en.wikipedia.org/wiki/Single_responsibility_principle ) karena membuat kelas
baru dari dua (atau lebih) kelas yang tidak saling mengenal satu sama lain;
 kami sangat menyarankan banyak pewarisan sebagai solusi terakhir dari semua
kemungkinan solusi - jika Anda benar-benar membutuhkan banyak fungsi berbeda yang
ditawarkan oleh kelas yang berbeda, komposisi mungkin merupakan alternatif yang lebih
baik.
Berlian dan mengapa Anda tidak menginginkannya
Spektrum masalah yang mungkin datang dari pewarisan berganda diilustrasikan oleh masalah
klasik bernama masalah berlian . Namanya mencerminkan bentuk diagram warisan - lihat
gambarnya.
 ada superclass paling top bernama A;
 ada dua subclass yang diturunkan dari A - B dan C;
 dan ada juga subclass paling bawah bernama D, berasal dari B dan C (atau C dan B,
karena dua varian ini memiliki arti berbeda dalam Python)
Bisakah kamu melihat berlian di sana?

Python, bagaimanapun, tidak suka berlian, dan tidak akan membiarkan Anda menerapkan hal
seperti ini. Jika Anda mencoba membangun hierarki seperti ini:
class A: pass class B(A): pass class C(A): pass class D(A, B): pass d = D()

Anda akan mendapatkan pengecualian TypeError , bersama dengan pesan berikut:


Cannot create a consistent method resolution order (MRO) for bases B, A

di mana MROsingkatan dari Method Resolution Order - ini adalah algoritma yang digunakan
Python untuk mencari pohon warisan untuk menemukan metode yang diperlukan.

Berlian sangat berharga dan berharga ... tetapi tidak dalam pemrograman. Hindari mereka
untuk kebaikan Anda sendiri.

6.1.6.1 Exceptions once again


Lebih lanjut tentang pengecualian
Membahas pemrograman objek menawarkan peluang yang sangat baik untuk kembali ke
pengecualian. Sifat objektif pengecualian Python menjadikannya alat yang sangat fleksibel,
dapat disesuaikan dengan kebutuhan spesifik, bahkan yang belum Anda ketahui.

Sebelum kita terjun ke wajah objektif pengecualian , kami ingin menunjukkan kepada Anda
beberapa aspek sintaksis dan semantik dari cara Python memperlakukan blok coba-kecuali,
karena ia menawarkan sedikit lebih banyak dari apa yang telah kami sajikan sejauh ini.

Fitur pertama yang ingin kita diskusikan di sini adalah cabang tambahan yang memungkinkan
yang dapat ditempatkan di dalam (atau lebih tepatnya, tepat di belakang) blok coba-kecuali -
ini adalah bagian dari kode yang dimulai dengan else- seperti dalam contoh di editor.

Kode berlabel dengan cara ini dijalankan ketika (dan hanya ketika) tidak ada pengecualian
yang dimunculkan di dalam try:bagian tersebut. Kita dapat mengatakan bahwa tepat satu
cabang dapat dieksekusi setelah try:- baik yang dimulai dengan except(jangan lupa bahwa
mungkin ada lebih dari satu cabang semacam ini) atau yang dimulai dengan else.
Catatan: else:cabang harus ditempatkan setelah exceptcabang terakhir .

Kode contoh menghasilkan output berikut:


Everything went fine 0.5 Division failed None
6.1.6.2 Exceptions once again
Lebih lanjut tentang pengecualian
Blok coba-kecuali dapat diperluas dengan satu cara lagi - dengan menambahkan bagian yang
dikepalai oleh finallykata kunci (itu harus cabang terakhir dari kode yang dirancang untuk
menangani pengecualian).

Catatan: dua varian ini ( elsedan finally) tidak tergantung dengan cara apa pun, dan mereka
dapat hidup berdampingan atau terjadi secara independen.

The finallyblock selalu dieksekusi (tim tersebut menyelesaikan try-kecuali eksekusi blok, maka
namanya), tidak peduli apa yang terjadi sebelumnya, bahkan ketika menaikkan pengecualian,
tidak peduli apakah ini sudah ditangani atau tidak.

Lihatlah kode di editor. Ini menghasilkan:


Everything went fine It's time to say good bye 0.5 Division failed It's time to say good bye None

6.1.6.3 Exceptions once again


Pengecualian adalah kelas
Semua contoh sebelumnya puas dengan mendeteksi jenis pengecualian tertentu dan
menanggapinya dengan cara yang tepat. Sekarang kita akan mempelajari lebih dalam, dan
melihat ke dalam pengecualian itu sendiri.

Anda mungkin tidak akan terkejut mengetahui bahwa pengecualian adalah kelas . Lebih jauh
lagi, ketika sebuah eksepsi dimunculkan, objek kelas akan dipakai, dan melewati semua level
eksekusi program, mencari cabang kecuali yang siap untuk menghadapinya.

Objek seperti itu membawa beberapa informasi berguna yang dapat membantu Anda
mengidentifikasi dengan tepat semua aspek dari situasi yang tertunda. Untuk mencapai tujuan
itu, Python menawarkan varian khusus klausa pengecualian - Anda dapat menemukannya di
editor.

Seperti yang Anda lihat, exceptpernyataan diperluas, dan berisi frasa tambahan dimulai
dengan askata kunci, diikuti oleh pengenal. Pengidentifikasi dirancang untuk menangkap objek
pengecualian sehingga Anda dapat menganalisis sifatnya dan menarik kesimpulan yang tepat.

Catatan: ruang lingkup pengidentifikasi mencakup exceptcabangnya, dan tidak melangkah


lebih jauh.

Contoh ini menyajikan cara yang sangat sederhana untuk menggunakan objek yang diterima -
cukup cetak saja (seperti yang Anda lihat, output dihasilkan oleh __str__()metode objek) dan
berisi pesan singkat yang menjelaskan alasannya.

Pesan yang sama akan dicetak jika tidak ada exceptblok pas dalam kode, dan Python terpaksa
menanganinya sendiri.

6.1.6.4 Exceptions once again


Pengecualian adalah kelas
Semua pengecualian Python bawaan membentuk hierarki kelas. Tidak ada halangan untuk
memperpanjangnya jika Anda menganggapnya masuk akal.
Lihatlah kode di editor.
Program ini membuang semua kelas pengecualian yang telah ditentukan sebelumnya dalam
bentuk cetakan mirip pohon.

Karena pohon adalah contoh sempurna dari struktur data rekursif , rekursi tampaknya menjadi
alat terbaik untuk melewatinya. The printExcTree()Fungsi membutuhkan dua argumen:
 titik di dalam pohon dari mana kita mulai melintasi pohon;
 tingkat bersarang (kami akan menggunakannya untuk membangun gambar cabang
pohon yang disederhanakan)

Mari kita mulai dari akar pohon - akar dari kelas pengecualian Python
adalah BaseExceptionkelas (itu adalah superclass dari semua pengecualian lainnya).
Untuk setiap kelas yang ditemui, lakukan serangkaian operasi yang sama:
 cetak namanya, diambil dari __name__properti;
 iterate melalui daftar subclass yang dikirimkan oleh __subclasses__()metode, dan secara
rekursif memanggil printExcTree()fungsi, masing-masing menambah level nesting.
Perhatikan bagaimana kami menggambar cabang dan garpu. Cetakan tidak diurutkan
dengan cara apa pun - Anda dapat mencoba mengurutkannya sendiri, jika Anda
menginginkan tantangan. Selain itu, ada beberapa ketidakakuratan yang halus dalam
cara beberapa cabang disajikan. Itu bisa diperbaiki juga, jika Anda mau.
Begini tampilannya:
BaseException
+---Exception
| +---TypeError
| +---StopAsyncIteration
| +---StopIteration
| +---ImportError
| | +---ModuleNotFoundError
| | +---ZipImportError
| +---OSError
| | +---ConnectionError
| | | +---BrokenPipeError
| | | +---ConnectionAbortedError
| | | +---ConnectionRefusedError
| | | +---ConnectionResetError
| | +---BlockingIOError
| | +---ChildProcessError
| | +---FileExistsError
| | +---FileNotFoundError
| | +---IsADirectoryError
| | +---NotADirectoryError
| | +---InterruptedError
| | +---PermissionError
| | +---ProcessLookupError
| | +---TimeoutError
| | +---UnsupportedOperation
| | +---herror
| | +---gaierror
| | +---timeout
| | +---Error
| | | +---SameFileError
| | +---SpecialFileError
| | +---ExecError
| | +---ReadError
| +---EOFError
| +---RuntimeError
| | +---RecursionError
| | +---NotImplementedError
| | +---_DeadlockError
| | +---BrokenBarrierError
| +---NameError
| | +---UnboundLocalError
| +---AttributeError
| +---SyntaxError
| | +---IndentationError
| | | +---TabError
| +---LookupError
| | +---IndexError
| | +---KeyError
| | +---CodecRegistryError
| +---ValueError
| | +---UnicodeError
| | | +---UnicodeEncodeError
| | | +---UnicodeDecodeError
| | | +---UnicodeTranslateError
| | +---UnsupportedOperation
| +---AssertionError
| +---ArithmeticError
| | +---FloatingPointError
| | +---OverflowError
| | +---ZeroDivisionError
| +---SystemError
| | +---CodecRegistryError
| +---ReferenceError
| +---BufferError
| +---MemoryError
| +---Warning
| | +---UserWarning
| | +---DeprecationWarning
| | +---PendingDeprecationWarning
| | +---SyntaxWarning
| | +---RuntimeWarning
| | +---FutureWarning
| | +---ImportWarning
| | +---UnicodeWarning
| | +---BytesWarning
| | +---ResourceWarning
| +---error
| +---Verbose
| +---Error
| +---TokenError
| +---StopTokenizing
| +---Empty
| +---Full
| +---_OptionError
| +---TclError
| +---SubprocessError
| | +---CalledProcessError
| | +---TimeoutExpired
| +---Error
| | +---NoSectionError
| | +---DuplicateSectionError
| | +---DuplicateOptionError
| | +---NoOptionError
| | +---InterpolationError
| | | +---InterpolationMissingOptionError
| | | +---InterpolationSyntaxError
| | | +---InterpolationDepthError
| | +---ParsingError
| | | +---MissingSectionHeaderError
| +---InvalidConfigType
| +---InvalidConfigSet
| +---InvalidFgBg
| +---InvalidTheme
| +---EndOfBlock
| +---BdbQuit
| +---error
| +---_Stop
| +---PickleError
| | +---PicklingError
| | +---UnpicklingError
| +---_GiveupOnSendfile
| +---error
| +---LZMAError
| +---RegistryError
| +---ErrorDuringImport
+---GeneratorExit
+---SystemExit
+---KeyboardInterrupt

6.1.6.5 Exceptions once again


Detail anatomi pengecualian
Mari kita melihat lebih dekat objek pengecualian, karena ada beberapa elemen yang sangat
menarik di sini (kita akan kembali ke masalah segera ketika kita mempertimbangkan teknik basis
input / output Python, karena subsistem eksepsi mereka sedikit memperluas objek-objek ini).

The BaseExceptionkelas memperkenalkan properti bernama args. Ini adalah tuple yang
dirancang untuk mengumpulkan semua argumen yang diteruskan ke konstruktor kelas . Itu
kosong jika konstruk telah dipanggil tanpa argumen, atau berisi hanya satu elemen ketika
konstruktor mendapatkan satu argumen (kami tidak menghitung selfargumen di sini), dan
seterusnya.

Kami telah menyiapkan fungsi sederhana untuk mencetak argsproperti dengan cara yang
elegan. Anda dapat melihat fungsinya di editor.

Kami telah menggunakan fungsi ini untuk mencetak konten argsproperti dalam tiga kasus
berbeda, di mana pengecualian Exceptionkelas dimunculkan dalam tiga cara berbeda. Untuk
membuatnya lebih spektakuler, kami juga telah mencetak objek itu sendiri, bersama dengan
hasil __str__()doa.
Kasus pertama terlihat rutin - hanya ada nama Pengecualian setelah raisekata kunci. Ini berarti
bahwa objek dari kelas ini telah dibuat dengan cara yang paling rutin.
Kasus kedua dan ketiga mungkin terlihat sedikit aneh pada pandangan pertama, tetapi tidak
ada yang aneh di sini - ini hanya doa konstruktor. Dalam raisepernyataan kedua , konstruktor
dipanggil dengan satu argumen, dan di argumen ketiga, dengan dua.
Seperti yang Anda lihat, output program mencerminkan ini, menunjukkan konten argsproperti
yang sesuai:
: : my exception : my exception : my exception ('my', 'exception') : ('my', 'exception') : ('my',
'exception')

6.1.6.6 Exceptions once again


Cara membuat pengecualian Anda sendiri
Hirarki pengecualian tidak tertutup atau selesai, dan Anda selalu dapat memperluasnya jika
Anda ingin atau perlu membuat dunia Anda sendiri yang dipenuhi dengan pengecualian Anda
sendiri.

Ini mungkin berguna ketika Anda membuat modul kompleks yang mendeteksi kesalahan dan
menimbulkan pengecualian, dan Anda ingin pengecualian mudah dibedakan dari yang lain
yang dibawa oleh Python.

Ini dilakukan dengan mendefinisikan pengecualian baru Anda sendiri sebagai subclass yang
berasal dari yang sudah ditentukan sebelumnya .
Catatan: jika Anda ingin membuat pengecualian yang akan digunakan sebagai kasus khusus
pengecualian bawaan apa pun, turunkan dari satu ini saja. Jika Anda ingin membangun hierarki
Anda sendiri, dan tidak ingin itu terkait erat dengan pohon pengecualian Python, turunkan dari
salah satu kelas pengecualian teratas, seperti Pengecualian .

Bayangkan bahwa Anda telah membuat aritmatika baru, diperintah oleh hukum dan teorema
Anda sendiri. Jelas bahwa pembagian telah didefinisikan ulang juga, dan harus berperilaku
dengan cara yang berbeda dari pembagian rutin. Jelas juga bahwa divisi baru ini harus
mengeluarkan pengecualiannya sendiri, berbeda dari ZeroDivisionError bawaan , tetapi masuk
akal untuk mengasumsikan bahwa dalam beberapa keadaan, Anda (atau pengguna aritmatika
Anda) mungkin ingin memperlakukan semua divisi nol dengan cara yang sama.

Permintaan seperti ini dapat dipenuhi dengan cara yang disajikan dalam editor. Lihatlah
kodenya, dan mari kita menganalisisnya:
 Kami telah menetapkan pengecualian kami sendiri, yang dinamai MyZeroDivisionError,
berasal dari bawaan ZeroDivisionError. Seperti yang Anda lihat, kami memutuskan untuk
tidak menambahkan komponen baru ke kelas.

Akibatnya, pengecualian kelas ini dapat - tergantung pada sudut pandang yang
diinginkan - diperlakukan seperti ZeroDivisionError polos , atau dianggap secara terpisah.

 The doTheDivision()Fungsi menimbulkan


baik MyZeroDivisionErroratau ZeroDivisionErrorpengecualian, tergantung pada nilai
argumen.

Fungsi dipanggil empat kali secara total, sedangkan dua doa pertama ditangani hanya
menggunakan satu exceptcabang (yang lebih umum) dan dua yang terakhir dengan
dua cabang yang berbeda, mampu membedakan pengecualian (jangan lupa: urutan
cabang membuat perbedaan mendasar!)

6.1.6.7 Exceptions once again


Cara membuat pengecualian Anda sendiri: lanjutan
Ketika Anda akan membangun alam semesta yang benar-benar baru yang diisi dengan
makhluk yang sama sekali baru yang tidak memiliki kesamaan dengan semua hal yang sudah
dikenal, Anda mungkin ingin membangun struktur pengecualian Anda sendiri .

Misalnya, jika Anda bekerja pada sistem simulasi besar yang dimaksudkan untuk memodelkan
kegiatan restoran pizza, dapat diinginkan untuk membentuk hierarki pengecualian yang terpisah.
Anda bisa mulai membangunnya dengan mendefinisikan pengecualian umum sebagai kelas
dasar baru untuk pengecualian khusus lainnya. Kami telah melakukannya dengan cara berikut:
class PizzaError(Exception): def __init__(self, pizza, message): Exception.__init__(message)
self.pizza = pizza

Catatan: kami akan mengumpulkan informasi lebih spesifik di sini daripada Pengecualian biasa ,
sehingga konstruktor kami akan mengambil dua argumen:
 satu menetapkan pizza sebagai subjek dari proses,
 dan satu berisi deskripsi masalah yang kurang lebih tepat.

Seperti yang Anda lihat, kami meneruskan parameter kedua ke konstruktor superclass, dan
menyimpan yang pertama di dalam properti kami sendiri.

Masalah yang lebih spesifik (seperti kelebihan keju) dapat memerlukan pengecualian yang lebih
spesifik. Dimungkinkan untuk mengambil kelas baru dari kelas yang sudah didefinisikan PizzaError,
seperti yang telah kita lakukan di sini:
class TooMuchCheeseError(PizzaError): def __init__(self, pizza, cheese, message):
PizzaError._init__(self, pizza, message) self.cheese = cheese

The TooMuchCheeseErrorpengecualian membutuhkan informasi lebih banyak daripada


biasa PizzaErrorpengecualian, jadi kami menambahkannya ke constructor -
nama cheesetersebut kemudian disimpan untuk diproses lebih lanjut.

6.1.6.8 Exceptions once again


Cara membuat pengecualian Anda sendiri: lanjutan
Lihatlah kode di editor. Kami telah menggabungkan bersama dua pengecualian yang
sebelumnya ditetapkan dan memanfaatkannya untuk bekerja dalam cuplikan contoh kecil.

Salah satunya muncul di dalam makePizza()fungsi ketika salah satu dari dua situasi yang salah ini
ditemukan: permintaan pizza yang salah, atau permintaan terlalu banyak keju.
catatan:
 menghapus cabang yang dimulai dengan except TooMuchCheeseErrorakan
menyebabkan semua pengecualian yang muncul diklasifikasikan sebagai PizzaError;
 menghapus cabang dimulai dengan except
PizzaErrorwillmenyebabkan TooMuchCheeseErrorpengecualian tetap tidak tertangani,
dan akan menyebabkan program berakhir.

Solusi sebelumnya, meskipun elegan dan efisien, memiliki satu kelemahan penting. Karena cara
mendeklarasikan konstruktor yang agak mudah, pengecualian baru tidak dapat digunakan apa
adanya, tanpa daftar lengkap dari argumen yang diperlukan.

Kami akan menghapus kelemahan ini dengan menetapkan nilai default untuk semua parameter
konstruktor . Lihatlah:

class PizzaError(Exception): def __init__(self, pizza='uknown', message=''): Exception.__init__(self,


message) self.pizza = pizza class TooMuchCheeseError(PizzaError): def __init__(self, pizza='uknown',
cheese='>100', message=''): PizzaError.__init__(self, pizza, message) self.cheese = cheese def
makePizza(pizza, cheese): if pizza not in ['margherita', 'capricciosa', 'calzone']: raise PizzaError if
cheese > 100: raise TooMuchCheeseError print("Pizza ready!") for (pz, ch) in [('calzone', 0),
('margherita', 110), ('mafia', 20)]: try: makePizza(pz, ch) except TooMuchCheeseError as tmce:
print(tmce, ':', tmce.cheese) except PizzaError as pe: print(pe, ':', pe.pizza)

Sekarang, jika keadaan memungkinkan, dimungkinkan untuk menggunakan nama kelas saja.

6.1.7.1 Generators and closures


Generator - di mana menemukannya
Generator - apa yang Anda kaitkan dengan kata ini? Mungkin merujuk ke beberapa perangkat
elektronik. Atau mungkin mengacu pada mesin berat dan serius yang dirancang untuk
menghasilkan daya, listrik, atau lainnya.

Generator Python adalah bagian dari kode khusus yang dapat menghasilkan serangkaian nilai,
dan untuk mengontrol proses iterasi . Inilah sebabnya mengapa generator sering
disebut iterator , dan meskipun beberapa mungkin menemukan perbedaan yang sangat halus
antara keduanya, kami akan memperlakukan mereka sebagai satu.

Anda mungkin tidak menyadarinya, tetapi Anda telah menemukan generator berkali-kali
sebelumnya. Lihatlah cuplikan yang sangat sederhana:
for i in range(5): print(i)

The range()fungsi, pada kenyataannya, generator, yang (sebenarnya, lagi) iterator


Apa bedanya?

Suatu fungsi mengembalikan satu, nilai yang didefinisikan dengan baik - mungkin merupakan
hasil dari evaluasi yang kurang lebih kompleks, misalnya, polinomial, dan dipanggil sekali - hanya
sekali.

Generator mengembalikan serangkaian nilai , dan secara umum, (secara implisit) dipanggil
lebih dari sekali.

Dalam contoh tersebut, range()generator dipanggil enam kali, memberikan lima nilai berikutnya
dari nol hingga empat, dan akhirnya menandakan bahwa rangkaian telah selesai.
Proses di atas sepenuhnya transparan. Mari kita beri penjelasan. Mari kita tunjukkan protokol
iterator .

6.1.7.2 Generators and closures


Generator - di mana menemukannya: lanjutan
The protokol iterator adalah cara di mana obyek harus bersikap agar sesuai dengan aturan-
aturan yang diberlakukan oleh konteks fordan inpernyataan . Objek yang sesuai dengan
protokol iterator disebut iterator.

Iterator harus menyediakan dua metode:


 __iter__()yang harus mengembalikan objek itu sendiridan yang dipanggil sekali
(diperlukan untuk Python untuk berhasil memulai iterasi)
 __next__()yang dimaksudkan untuk mengembalikan nilai berikutnya (pertama, kedua,
dan seterusnya) dari seri yang diinginkan - itu akan dipanggil oleh for/ inpernyataan
untuk melewati iterasi berikutnya; jika tidak ada lagi nilai untuk diberikan, metode
harus memunculkan StopIterationpengecualian .

Apakah itu terdengar aneh? Tidak semuanya. Lihatlah contoh di editor.


Kami telah membangun sebuah kelas yang dapat melakukan iterasi melalui nilai pertama n(di
mana nmerupakan parameter konstruktor) dari angka Fibonacci.
Biarkan kami mengingatkan Anda - angka Fibonacci ( Fib i ) didefinisikan sebagai berikut:
Fib 1 = 1
Fib 2 = 1
Fib i = Fib i-1 + Fib i-2

Dengan kata lain:


 dua angka Fibonacci pertama sama dengan 1;
 setiap angka Fibonacci lainnya adalah jumlah dari dua yang sebelumnya (misalnya,
Fib 3 = 2, Fib 4 = 3, Fib 5 = 5, dan seterusnya)

Mari selami kode:


 baris 2 hingga 6: konstruktor kelas mencetak pesan (kami akan menggunakan ini untuk
melacak perilaku kelas), menyiapkan beberapa variabel ( __nuntuk menyimpan batas
seri, __iuntuk melacak angka Fibonacci saat ini untuk memberikan, dan __p1bersama
dengan __p2untuk menyimpan keduanya nomor sebelumnya);
 baris 8 hingga 10: __iter__metode ini wajib mengembalikan objek iterator itu
sendiri; tujuannya mungkin agak ambigu di sini, tetapi tidak ada misteri; coba bayangkan
sebuah objek yang bukan iterator (mis. itu adalah kumpulan dari beberapa entitas),
tetapi salah satu komponennya adalah iterator yang dapat memindai
koleksi; yang __iter__metode harus mengekstrak iterator dan mempercayakan dengan
pelaksanaan protokol iterasi ; seperti yang Anda lihat, metode ini memulai aksinya
dengan mencetak pesan;
 baris 12 hingga 21: __next__metode ini bertanggung jawab untuk membuat urutan; ini
agak bertele-tele, tetapi ini harus membuatnya lebih mudah dibaca; pertama, ia
mencetak pesan, kemudian memperbarui jumlah nilai yang diinginkan, dan jika
mencapai akhir urutan, metode ini memutus iterasi dengan menaikkan pengecualian
StopIteration; kode lainnya sederhana, dan itu mencerminkan dengan tepat definisi yang
kami tunjukkan sebelumnya;
 baris 23 dan 24 memanfaatkan iterator.

Kode menghasilkan output berikut:


__init__ __iter__ __next__ 1 __next__ 1 __next__ 2 __next__ 3 __next__ 5 __next__ 8 __next__ 13
__next__ 21 __next__ 34 __next__ 55 __next__

Melihat:
 objek iterator dipakai lebih dulu;
 selanjutnya, Python memanggil __iter__metode untuk mendapatkan akses ke iterator
yang sebenarnya;
 yang __next__metode dipanggil sebelas kali - yang pertama sepuluh kali menghasilkan
nilai-nilai yang bermanfaat, sedangkan kesebelas berakhir iterasi.

6.1.7.3 Generators and closures


Generator - di mana menemukannya: lanjutan
Contoh sebelumnya menunjukkan Anda solusi di mana objek iterator adalah bagian dari kelas
yang lebih kompleks.

Kode ini tidak benar-benar canggih, tetapi menyajikan konsep dengan cara yang jelas.
Lihatlah kode di editor.

Kami telah membangun Fibiterator ke dalam kelas lain (kita dapat mengatakan bahwa kita
telah mengkomposisikannya ke dalam Classkelas). Ini dipakai bersama dengan Classobjek.

Objek kelas dapat digunakan sebagai iterator ketika (dan hanya ketika) itu menjawab
positif __iter__doa - kelas ini dapat melakukannya, dan jika itu dipanggil dengan cara ini, ia
menyediakan objek yang dapat mematuhi protokol iterasi.
Inilah sebabnya mengapa output kode sama dengan sebelumnya, meskipun objek Fibkelas
tidak digunakan secara eksplisit di dalam forkonteks loop.

6.1.7.4 Generators and closures


The yield pernyataan
Protokol iterator tidak terlalu sulit untuk dipahami dan digunakan, tetapi protokol ini juga tidak
terbantahkan .
Ketidaknyamanan utama yang dibawa adalah kebutuhan untuk menyelamatkan keadaan
iterasi di antara __iter__doa berikutnya .

Misalnya, Fibiterator dipaksa untuk secara tepat menyimpan tempat di mana doa terakhir telah
dihentikan (yaitu, jumlah yang dievaluasi dan nilai-nilai dari dua elemen sebelumnya). Ini
membuat kode lebih besar dan kurang dimengerti.

Inilah sebabnya mengapa Python menawarkan cara menulis iterator yang jauh lebih efektif,
nyaman, dan elegan.
Konsep ini pada dasarnya didasarkan pada mekanisme yang sangat spesifik dan kuat yang
disediakan oleh yieldkata kunci.

Anda mungkin menganggap yieldkata kunci sebagai saudara yang lebih cerdas
dari returnpernyataan itu, dengan satu perbedaan penting.
Lihatlah fungsi ini:
def fun(n): for i in range(n): return i

Terlihat aneh, bukan? Sudah jelas bahwa forloop tidak memiliki kesempatan untuk
menyelesaikan eksekusi pertama, karena returnakan merusaknya tidak dapat dibatalkan.
Selain itu, menjalankan fungsi tidak akan mengubah apa pun - forloop akan mulai dari awal dan
akan segera rusak.

Kita dapat mengatakan bahwa fungsi seperti itu tidak dapat menyimpan dan mengembalikan
kondisinya di antara permintaan berikutnya.
Ini juga berarti bahwa fungsi seperti ini tidak dapat digunakan sebagai generator .

Kami telah mengganti tepat satu kata dalam kode - dapatkah Anda melihatnya?
def fun(n): for i in range(n): yield i

Kami telah menambahkan yieldsebagai gantinya return. Amandemen kecil ini mengubah
fungsi menjadi generator , dan menjalankan yieldpernyataan memiliki beberapa efek yang
sangat menarik.

Pertama-tama, ini memberikan nilai ekspresi yang ditentukan setelah yieldkata kunci,
seperti return, tetapi tidak kehilangan status fungsi.
Semua nilai variabel dibekukan, dan tunggu permintaan berikutnya, ketika eksekusi dilanjutkan
(tidak diambil dari awal, seperti setelah return).

Ada satu batasan penting: fungsi seperti itu tidak boleh dipanggil secara eksplisit karena - pada
kenyataannya - itu bukan fungsi lagi; itu adalah objek generator .

Doa akan mengembalikan pengidentifikasi objek , bukan seri yang kita harapkan dari generator.
Karena alasan yang sama, fungsi sebelumnya (yang dengan returnpernyataan) hanya dapat
dipanggil secara eksplisit, dan tidak boleh digunakan sebagai generator.

Cara membangun generator


Mari kita tunjukkan generator baru dalam aksi.
Ini adalah bagaimana kita dapat menggunakannya:
def fun(n): for i in range(n): yield i for v in fun(5): print(v)

Bisakah Anda menebak hasilnya?


Memeriksa
01234

6.1.7.5 Generators and closures


Cara membangun generator Anda sendiri
Bagaimana jika Anda membutuhkan generator untuk
menghasilkan kekuatan n pertama dari 2 ?
Tidak ada yang lebih mudah. Lihat saja kode di editor.
Bisakah Anda menebak hasilnya? Jalankan kode untuk memeriksa tebakan Anda.

Generator juga dapat digunakan dalam pemahaman daftar , seperti di sini:


def powersOf2(n): pow = 1 for i in range(n): yield pow pow *= 2 t = [x for x in powersOf2(5)] print(t)

Jalankan contoh dan periksa output.

The list()Fungsi dapat mengubah serangkaian doa Generator selanjutnya ke daftar nyata :
def powersOf2(n): pow = 1 for i in range(n): yield pow pow *= 2 t = list(powersOf2(3)) print(t)

Sekali lagi, cobalah untuk memprediksi output dan menjalankan kode untuk memeriksa prediksi
Anda.

Selain itu, konteks yang dibuat oleh inoperator memungkinkan Anda untuk menggunakan
generator juga.

Contoh menunjukkan bagaimana melakukannya:


def powersOf2(n): pow = 1 for i in range(n): yield pow pow *= 2 for i in range(20): if i in
powersOf2(4): print(i)

Apa output kode? Jalankan program dan periksa.

Sekarang mari kita lihat generator angka Fibonacci , dan memastikan bahwa itu terlihat jauh
lebih baik daripada versi objektif berdasarkan implementasi protokol iterator langsung.
Ini dia:
def Fib(n): p = pp = 1 for i in range(n): if i in [0, 1]: yield 1 else: n = p + pp pp, p = p, n yield n fibs =
list(Fib(10)) print(fibs)

Tebak output (daftar) yang dihasilkan oleh generator, dan jalankan kode untuk memeriksa
apakah Anda benar.

6.1.7.6 Generators and closures


Lebih lanjut tentang pemahaman daftar
Anda harus dapat mengingat aturan yang mengatur pembuatan dan penggunaan fenomena
Python yang sangat istimewa bernama daftar pemahaman - cara sederhana dan sangat
mengesankan untuk membuat daftar dan isinya .

Jika Anda membutuhkannya, kami telah memberikan pengingat cepat di editor.


Ada dua bagian di dalam kode, keduanya membuat daftar yang berisi beberapa kekuatan
alami pertama dari sepuluh.

Yang pertama menggunakan cara rutin memanfaatkan forloop, sedangkan yang terakhir
menggunakan daftar pemahaman dan membangun daftar di situ, tanpa perlu loop, atau kode
tambahan lainnya.

Sepertinya daftar dibuat di dalam dirinya sendiri - itu tidak benar, tentu saja, karena Python harus
melakukan operasi yang hampir sama seperti pada cuplikan pertama, tetapi tidak dapat
dibantah bahwa formalisme kedua hanya lebih elegan, dan memungkinkan pembaca
menghindari detail yang tidak perlu.
Contoh menghasilkan dua baris identik yang berisi teks berikut:
[1, 10, 100, 1000, 10000, 100000]

Jalankan kode untuk memeriksa apakah kita benar.


6.1.7.7 Generators and closures
Lebih lanjut tentang pemahaman daftar: lanjutan
Ada sintaks yang sangat menarik yang ingin kami tunjukkan sekarang. Kegunaannya tidak
terbatas pada daftar pemahaman, tetapi kita harus mengakui bahwa pemahaman adalah
lingkungan yang ideal untuk itu.

Ini adalah ekspresi kondisional - cara memilih salah satu dari dua nilai yang berbeda
berdasarkan hasil ekspresi Boolean .
Melihat:
expression_one jika syarat lain expression_two

Sekilas mungkin terlihat mengejutkan, tetapi Anda harus ingat bahwa ini bukan instruksi
bersyarat . Apalagi itu bukan instruksi sama sekali. Itu operator.

Nilai yang diberikannya sama dengan expression_one ketika kondisinya True,


dan expression_twosebaliknya.

Contoh yang baik akan memberi tahu Anda lebih banyak. Lihatlah kode di editor.
Kode mengisi daftar dengan 1's dan 0s - jika indeks elemen tertentu aneh, elemen diatur ke 0,
dan 1sebaliknya.

Sederhana? Mungkin tidak pada pandangan pertama. Anggun? Tak terbantahkan.


Bisakah Anda menggunakan trik yang sama dalam pemahaman daftar? Ya kamu bisa.

6.1.7.8 Generators and closures


Lebih lanjut tentang pemahaman daftar: lanjutan
Lihatlah contoh di editor.
Kekompakan dan keanggunan - dua kata ini muncul di benak ketika melihat kode.
Jadi, apa yang mereka miliki bersama, generator dan daftar pemahaman? Apakah ada
hubungan di antara mereka? Iya nih. Koneksi yang agak longgar, tetapi yang tegas.
Hanya satu perubahan dapat mengubah pemahaman apa pun menjadi generator .

Sekarang lihat kode di bawah ini dan lihat apakah Anda dapat menemukan detail yang
mengubah pemahaman daftar menjadi generator:
lst = [1 if x % 2 == 0 else 0 for x in range(10)] genr = (1 if x % 2 == 0 else 0 for x in range(10)) for v in
lst: print(v, end=" ") print() for v in genr: print(v, end=" ") print()

Ini tanda kurung . Kurung membuat pemahaman, kurung membuat generator.


Namun, kode tersebut ketika dijalankan, menghasilkan dua baris yang identik:
10101010101010101010

Bagaimana Anda bisa tahu bahwa tugas kedua membuat generator, bukan daftar?
Ada beberapa bukti yang bisa kami tunjukkan. Terapkan len()fungsi ke kedua entitas ini.
len(lst)akan dievaluasi untuk 10. Jelas dan dapat diprediksi. len(genr)akan memunculkan
pengecualian, dan Anda akan melihat pesan berikut:
TypeError: object of type 'generator' has no len()

Tentu saja, tidak perlu menyimpan daftar atau generator - Anda dapat membuatnya tepat di
tempat Anda membutuhkannya - seperti di sini:
for v in [1 if x % 2 == 0 else 0 for x in range(10)]: print(v, end=" ") print() for v in (1 if x % 2 == 0 else 0
for x in range(10)): print(v, end=" ") print()

Catatan: tampilan output yang sama tidak berarti bahwa kedua loop bekerja dengan cara yang
sama. Pada loop pertama, daftar dibuat (dan diulangi) secara keseluruhan - itu benar-benar
ada ketika loop sedang dieksekusi.
Pada loop kedua, tidak ada daftar sama sekali - hanya ada nilai selanjutnya yang dihasilkan oleh
generator, satu per satu.
Lakukan eksperimen Anda sendiri.

6.1.7.9 Generators and closures


The lambda fungsi
The lambdafungsi adalah konsep yang dipinjam dari matematika, lebih khusus, dari bagian yang
disebut kalkulus Lambda , tapi dua fenomena ini tidak sama.

Matematikawan menggunakan kalkulus Lambda dalam banyak sistem formal yang terhubung
dengan logika, rekursi, atau teorema. Pemrogram menggunakan lambdafungsi untuk
menyederhanakan kode, untuk membuatnya lebih jelas dan lebih mudah dimengerti.

Sebuah lambdafungsi adalah fungsi tanpa nama (Anda juga dapat menyebutnya fungsi
anonim ). Tentu saja, pernyataan seperti itu langsung menimbulkan pertanyaan: bagaimana
Anda menggunakan sesuatu yang tidak dapat diidentifikasi?

Untungnya, ini bukan masalah, karena Anda dapat memberi nama fungsi semacam itu jika Anda
benar-benar membutuhkannya, tetapi, pada kenyataannya, dalam banyak
kasus, lambdafungsi tersebut dapat ada dan berfungsi sambil tetap menggunakan
penyamaran sepenuhnya.

Deklarasi lambdafungsi tidak menyerupai deklarasi fungsi normal dengan cara apa pun - lihat
sendiri:
lambda parameters : expression

Klausa seperti itu mengembalikan nilai ekspresi ketika memperhitungkan nilai saat ini
dari lambdaargumen saat ini .

Seperti biasa, sebuah contoh akan sangat membantu. Contoh kami menggunakan
tiga lambdafungsi, tetapi memberi mereka nama. Lihatlah dengan cermat:
two = lambda : 2 sqr = lambda x : x * x pwr = lambda x, y : x ** y for a in range(-2, 3): print(sqr(a),
end=" ") print(pwr(a, two()))

Mari kita menganalisisnya:


 yang pertama lambdaadalah fungsi tanpa parameter anonim yang selalu
kembali 2. Seperti yang telah kita tetapkan ke variabel bernamatwo , kita dapat
mengatakan bahwa fungsi tersebut tidak anonim lagi, dan kita dapat menggunakan
nama untuk memintanya.
 yang kedua adalah fungsi anonim satu-parameter yang mengembalikan nilai argumen
kuadratnya. Kami juga menamainya seperti itu.
 yang ketiga lambda mengambil dua parameter dan mengembalikan nilai yang
pertama dinaikkan ke kekuatan yang kedua. Nama variabel yang
membawa lambdaberbicara untuk dirinya sendiri. Kami tidak menggunakan powuntuk
menghindari kebingungan dengan fungsi bawaan dengan nama dan tujuan yang sama.

Program menghasilkan output sebagai berikut:


4 4 1 1 0 0 1 1 4 4

Contoh ini cukup jelas untuk menunjukkan bagaimana lambdas dideklarasikan dan bagaimana
mereka berperilaku, tetapi tidak mengatakan mengapa mereka diperlukan, dan untuk apa
mereka digunakan, karena semuanya dapat diganti dengan fungsi Python rutin.
Di mana manfaatnya?

6.1.7.10 Generators and closures


Bagaimana cara menggunakan lambdas dan untuk apa?
Bagian paling menarik dari penggunaan lambdas muncul ketika Anda dapat menggunakannya
dalam bentuk murni - sebagai bagian kode anonim yang dimaksudkan untuk mengevaluasi
hasil .

Bayangkan bahwa kita membutuhkan suatu fungsi (kita akan menamainya printfunction) yang
mencetak nilai dari suatu fungsi (lainnya) yang diberikan untuk sekumpulan argumen yang dipilih.
Kami ingin printfunctionmenjadi universal - ia harus menerima serangkaian argumen yang
dimasukkan dalam daftar dan fungsi yang akan dievaluasi, baik sebagai argumen - kami tidak
ingin melakukan hardcode apa pun.

Lihatlah contoh di editor. Beginilah cara kami mengimplementasikan ide.


Mari kita analisa. The printfunction()Fungsi membutuhkan dua parameter:
 yang pertama, daftar argumen yang ingin kita cetak hasilnya;
 yang kedua, fungsi yang harus dipanggil sebanyak jumlah nilai yang dikumpulkan di
dalam parameter pertama.

Catatan: kami juga mendefinisikan fungsi yang dinamai poly()- ini adalah fungsi yang nilainya
akan kami cetak. Penghitungan fungsi yang dijalankannya tidak terlalu canggih - itu adalah
polinomial (karena itu namanya) dari suatu bentuk:
f (x) = 2x 2 - 4x + 2

Nama fungsi kemudian diteruskan ke printfunction()bersama dengan satu set dari lima argumen
yang berbeda - set dibangun dengan klausa pemahaman daftar.

Kode mencetak baris-baris berikut:


f(-2)=18 f(-1)=8 f(0)=2 f(1)=0 f(2)=2

Bisakah kita menghindari mendefinisikan poly()fungsi, karena kita tidak akan menggunakannya
lebih dari sekali? Ya, kita bisa - inilah manfaat yang bisa diberikan lambda.

Lihatlah contoh di bawah ini. Bisakah Anda melihat perbedaannya?


def printfunction(args, fun): for x in args: print('f(', x,')=', fun(x), sep='') printfunction([x for x in
range(-2, 3)], lambda x: 2 * x**2 - 4 * x + 2)

The printfunction()tetap persis sama, tetapi tidak ada poly()fungsi. Kita tidak perlu lagi, karena
jumlahnya banyak sekarang langsung di dalam printfunction()doa dalam bentuk lambda
didefinisikan dengan cara berikut: lambda x: 2 * x**2 - 4 * x + 2.

Kode menjadi lebih pendek, lebih jelas, dan lebih mudah dibaca.

Mari kita tunjukkan tempat lain di mana lambda bisa berguna. Kami akan mulai dengan
deskripsi map(), fungsi Python bawaan. Namanya tidak terlalu deskriptif, idenya sederhana, dan
fungsinya sendiri benar-benar dapat digunakan.

6.1.7.11 Generators and closures


Fungsi Lambdas dan map ()
Dalam kasus paling sederhana dari semua kemungkinan, map()fungsi mengambil dua argumen:
 sebuah fungsi;
 sebuah daftar.
map(function, list)

Deskripsi di atas sangat disederhanakan, seperti:


 map()argumen kedua dapat berupa entitas apa pun yang dapat diulang (misalnya,
tuple, atau hanya generator)
 map() dapat menerima lebih dari dua argumen.
The map()Fungsi berlaku fungsi disahkan oleh argumen pertama untuk semua elemen argumen
kedua ini, dan mengembalikan sebuah iterator memberikan semua hasil fungsi
berikutnya . Anda dapat menggunakan iterator yang dihasilkan dalam satu lingkaran, atau
mengubahnya menjadi daftar menggunakan list()fungsi.

Bisakah Anda melihat peran lambda di sini?

Lihatlah kode di editor - kami telah menggunakan dua lambda di dalamnya.

Inilah intriknya:
 membangun list1dengan nilai-nilai dari 0ke 4;
 selanjutnya, gunakan mapbersama dengan yang pertama lambdauntuk membuat
daftar baru di mana semua elemen telah dievaluasi sebagai 2dinaikkan ke daya yang
diambil dari elemen yang sesuai dari list1;
 list2 dicetak kemudian;
 pada langkah berikutnya, gunakan map()fungsi lagi untuk menggunakan generator
yang dikembalikan dan untuk langsung mencetak semua nilai yang diberikannya; seperti
yang Anda lihat, kami telah melibatkan yang kedua di lambdasini - itu hanya kuadrat
setiap elemen dari list2.

Coba bayangkan kode yang sama tanpa lambda. Apakah akan lebih baik? Tidak mungkin.

6.1.7.12 Generators and closures


Fungsi Lambdas dan filter ()
Fungsi Python lain yang dapat secara signifikan dipercantik dengan aplikasi lambda
adalah filter().

Itu mengharapkan jenis argumen yang sama seperti map(), tetapi melakukan sesuatu yang
berbeda - ia memfilter argumen kedua sambil dipandu oleh arah yang mengalir dari fungsi yang
ditentukan sebagai argumen pertama(fungsi dipanggil untuk setiap elemen daftar, seperti
di map()).

Elemen yang kembali Truedari fungsi lulus filter - yang lain ditolak.
Contoh di editor menunjukkan filter()fungsi dalam aksi.

Catatan: kami telah membuat penggunaan randommodul untuk menginisialisasi nomor acak
generator (tidak harus bingung dengan generator yang baru saja kita berbicara tentang)
dengan seed()fungsi, dan menghasilkan lima nilai integer acak dari -
10ke 10menggunakan randint()fungsi.

Daftar ini kemudian disaring, dan hanya angka-angka yang genap dan lebih besar dari nol yang
diterima.

Tentu saja, sepertinya Anda tidak akan menerima hasil yang sama, tetapi seperti inilah hasil kami:
[6, 3, 3, 2, -7] [6, 2]

6.1.7.13 Generators and closures


Sekilas tentang penutupan
Mari kita mulai dengan definisi: closure adalah teknik yang memungkinkan penyimpanan nilai
terlepas dari kenyataan bahwa konteks di mana mereka telah dibuat tidak ada
lagi . Rumit? Sedikit.

Mari kita analisis contoh sederhana:


def outer(par): loc = par var = 1 outer(var) print(var) print(loc)

Contohnya jelas salah.


Dua baris terakhir akan menyebabkan pengecualian NameError - tidak parjuga loctidak dapat
diakses di luar fungsi. Kedua variabel ada saat dan hanya ketika outer()fungsi dieksekusi.

Lihatlah contoh di editor. Kami telah memodifikasi kode secara signifikan.

Ada elemen baru di dalamnya - fungsi (bernama inner) di dalam fungsi lain (bernama outer).
Bagaimana cara kerjanya? Sama seperti fungsi lain kecuali untuk fakta yang inner()dapat
dipanggil hanya dari dalam outer(). Kita dapat mengatakan bahwa
itu inner()adalah outer()alat pribadi - tidak ada bagian lain dari kode yang dapat
mengaksesnya.

Perhatikan baik-baik:
 yang inner()fungsi mengembalikan nilai diakses variabel di dalam ruang lingkup,
seperti inner()dapat menggunakan salah satu entitas di pembuangan outer()
 yang outer()mengembalikan fungsi inner()fungsi itu sendiri; lebih tepatnya, itu
mengembalikan salinan inner()fungsi, yang dibekukan pada saat outer()doa; fungsi
beku berisi lingkungan penuhnya, termasuk keadaan semua variabel lokal, yang juga
berarti bahwa nilai locberhasil dipertahankan, meskipun sudah outer()tidak ada sejak
lama.
Akibatnya, kode ini sepenuhnya valid, dan menghasilkan:
1

Fungsi yang dikembalikan selama outer()doa adalah penutupan .

6.1.7.14 Generators and closures


Sekilas tentang penutupan: lanjutan
Penutupan harus dilakukan dengan cara yang persis sama dengan yang telah dinyatakan .
Pada contoh sebelumnya (lihat kode di bawah):
def outer(par): loc = par def inner(): return loc return inner var = 1 fun = outer(var) print(fun())

yang inner()fungsi adalah parameterless, jadi kami harus memanggil tanpa argumen.
Sekarang lihat kode di editor. Sangat mungkin untuk mendeklarasikan penutupan yang
dilengkapi dengan sejumlah parameter yang berubah-ubah , misalnya, seperti power()fungsi.
Ini berarti bahwa penutupan tidak hanya memanfaatkan lingkungan beku, tetapi juga
dapat mengubah perilakunya dengan menggunakan nilai yang diambil dari luar .

Contoh ini menunjukkan satu keadaan yang lebih menarik - Anda dapat membuat penutupan
sebanyak yang Anda inginkan menggunakan satu dan bagian kode yang sama . Ini dilakukan
dengan fungsi bernama makeclosure(). catatan:
 penutupan pertama yang diperoleh dari makeclosure()mendefinisikan alat
mengkuadratkan argumennya;
 yang kedua dirancang untuk kubus argumen.

Inilah sebabnya mengapa kode menghasilkan output berikut:


0 0 0 1 1 1 2 4 8 3 9 27 4 16 64

Lakukan tes Anda sendiri.

6.1.8.1 Processing files


Mengakses file dari kode Python
Salah satu masalah paling umum dalam pekerjaan pengembang adalah memproses data yang
disimpan dalam file sementara file biasanya disimpan secara fisik menggunakan perangkat
penyimpanan - hard disk, optik, jaringan, atau disk solid-state.
Sangat mudah untuk membayangkan program yang terdiri dari 20 angka, dan sama mudahnya
untuk membayangkan pengguna program ini memasukkan dua puluh angka ini langsung dari
keyboard.

Jauh lebih sulit untuk membayangkan tugas yang sama ketika ada 20.000 angka untuk disortir,
dan tidak ada satu pengguna pun yang bisa memasukkan angka-angka ini tanpa membuat
kesalahan.

Jauh lebih mudah untuk membayangkan bahwa angka-angka ini disimpan dalam file disk yang
dibaca oleh program. Program ini mengurutkan angka-angka dan tidak mengirimnya ke layar,
tetapi sebaliknya membuat file baru dan menyimpan urutan angka yang terurut di sana.

Jika kami ingin mengimplementasikan database sederhana, satu-satunya cara untuk


menyimpan informasi di antara program yang dijalankan adalah dengan menyimpannya ke
dalam file (atau file jika database Anda lebih kompleks).

Pada prinsipnya, masalah pemrograman yang tidak sederhana bergantung pada penggunaan
file, apakah itu memproses gambar (disimpan dalam file), mengalikan matriks (disimpan dalam
file), atau menghitung upah dan pajak (membaca data yang disimpan dalam file).

Anda mungkin bertanya mengapa kami menunggu sampai sekarang untuk menunjukkan
masalah ini kepada Anda.

Jawabannya sangat sederhana - cara Python mengakses dan memproses file


diimplementasikan menggunakan serangkaian objek yang konsisten. Tidak ada momen yang
lebih baik untuk membicarakannya.

6.1.8.2 Processing files


Nama file
Sistem operasi yang berbeda dapat menangani file dengan cara yang berbeda. Sebagai
contoh, Windows menggunakan konvensi penamaan yang berbeda dari yang diadopsi dalam
sistem Unix / Linux.

Jika kita menggunakan gagasan nama file kanonik (nama yang secara unik mendefinisikan
lokasi file terlepas dari levelnya di pohon direktori) kita dapat menyadari bahwa nama-nama ini
terlihat berbeda di Windows dan di Unix / Linux:

Seperti yang Anda lihat, sistem yang berasal dari Unix / Linux tidak menggunakan huruf disk drive
(misalnya, C:) dan semua direktori tumbuh dari satu direktori root yang disebut /, sementara
sistem Windows mengenali direktori root sebagai \.

Selain itu, nama file sistem Unix / Linux adalah case-sensitive. Sistem Windows menyimpan huruf
yang digunakan dalam nama file, tetapi tidak membedakan antara hurufnya sama sekali.
Ini berarti bahwa dua string ini:

ThisIsTheNameOfTheFile
dan

thisisthenameofthefile
menjelaskan dua file berbeda di sistem Unix / Linux, tetapi nama yang sama untuk hanya satu
file di sistem Windows.

Perbedaan utama dan paling mencolok adalah Anda harus menggunakan dua pemisah yang
berbeda untuk nama direktori : \di Windows, dan /di Unix / Linux.

Perbedaan ini tidak terlalu penting bagi pengguna normal, tetapi sangat penting ketika menulis
program dengan Python .

Untuk memahami alasannya, cobalah untuk mengingat peran yang sangat spesifik yang
dimainkan oleh \string Python di dalam.

6.1.8.3 Processing files


Nama file: lanjutan
Misalkan Anda tertarik pada file tertentu yang terletak di direktori dir , dan bernama file .
Misalkan Anda ingin menetapkan string yang berisi nama file.

Dalam sistem Unix / Linux, mungkin terlihat sebagai berikut:


name = "/dir/file"

Tetapi jika Anda mencoba kode untuk sistem Windows:


name = "\dir\file"

Anda akan mendapatkan kejutan yang tidak menyenangkan: baik Python akan menghasilkan
kesalahan, atau eksekusi program akan berperilaku aneh, seolah-olah nama file telah terdistorsi
dalam beberapa cara.

Sebenarnya, itu tidak aneh sama sekali, tetapi cukup jelas dan alami. Python menggunakan
karakter \sebagai pelarian (seperti \n).

Ini berarti bahwa nama file Windows harus ditulis sebagai berikut:
name = "\\dir\\file"

Untungnya, ada juga satu solusi lagi. Python cukup pintar untuk dapat mengubah garis miring
menjadi garis miring terbalik setiap kali ditemukan bahwa itu diperlukan oleh OS.

Ini berarti bahwa setiap tugas berikut:


name = "/dir/file" name = "c:/dir/file"

akan bekerja dengan Windows juga.


Program apa pun yang ditulis dengan Python (dan tidak hanya dalam Python, karena konvensi
itu berlaku untuk hampir semua bahasa pemrograman) tidak berkomunikasi dengan file secara
langsung, tetapi melalui beberapa entitas abstrak yang diberi nama berbeda dalam bahasa
atau lingkungan yang berbeda - istilah yang paling banyak digunakan
adalah handle atau stream(kami akan menggunakannya sebagai sinonim di sini).
Programmer, yang memiliki sekumpulan fungsi / metode yang lebih banyak atau kurang kaya,
dapat melakukan operasi tertentu pada stream, yang memengaruhi file nyata menggunakan
mekanisme yang terdapat dalam kernel sistem operasi.

Dengan cara ini, Anda dapat menerapkan proses mengakses file apa pun, bahkan ketika nama
file tidak diketahui pada saat menulis program.
Operasi yang dilakukan dengan aliran abstrak mencerminkan aktivitas yang terkait dengan file
fisik.

Untuk menghubungkan (mengikat) aliran dengan file, perlu untuk melakukan operasi eksplisit.
Operasi menghubungkan aliran dengan file disebut membuka file , sementara memutus tautan
ini dinamai menutup file .

Oleh karena itu, kesimpulannya adalah bahwa operasi pertama yang dilakukan di sungai
selalu opendan yang terakhir adalah close. Program ini, pada dasarnya, bebas untuk
memanipulasi aliran antara dua peristiwa ini dan untuk menangani file yang terkait.
Kebebasan ini tentu saja dibatasi oleh karakteristik fisik file dan cara file dibuka.

Mari kita katakan lagi bahwa pembukaan aliran bisa gagal, dan itu bisa terjadi karena beberapa
alasan: yang paling umum adalah kurangnya file dengan nama yang ditentukan.
Bisa juga terjadi bahwa file fisik ada, tetapi program tidak diizinkan untuk membukanya. Ada juga
risiko bahwa program telah membuka terlalu banyak aliran, dan sistem operasi spesifik mungkin
tidak memungkinkan pembukaan simultan lebih dari n file (misalnya, 200).
Program yang ditulis dengan baik harus mendeteksi celah yang gagal ini, dan bereaksi sesuai itu.

6.1.8.4 Processing files


File stream
Pembukaan aliran tidak hanya terkait dengan file, tetapi juga harus menyatakan cara di mana
aliran akan diproses. Deklarasi ini disebut mode terbuka .

Jika pembukaan berhasil, program akan diizinkan untuk melakukan hanya operasi yang
konsisten dengan mode terbuka yang dinyatakan .

Ada dua operasi dasar yang dilakukan pada stream:


 dibaca dari aliran: bagian data diambil dari file dan ditempatkan di area memori yang
dikelola oleh program (misalnya, variabel);
 tulis ke aliran: bagian data dari memori (misalnya, variabel) ditransfer ke file.

Ada tiga mode dasar yang digunakan untuk membuka aliran:


 mode baca : aliran yang dibuka dalam mode ini memungkinkan hanya operasi
baca ; mencoba menulis ke aliran akan menyebabkan pengecualian (pengecualian
bernama UnsupportedOperation , yang mewarisi OSError dan ValueError , dan berasal
dari modul io );
 mode tulis : aliran yang dibuka dalam mode ini hanya memungkinkan operasi
tulis ; mencoba membaca aliran akan menyebabkan pengecualian yang disebutkan di
atas;
 mode pembaruan : aliran yang dibuka dalam mode ini memungkinkan penulisan dan
pembacaan .

6.1.8.5 Processing files


Menangani file
Python mengasumsikan bahwa setiap file tersembunyi di balik objek dari kelas yang memadai .
Tentu saja, sulit untuk tidak bertanya bagaimana menafsirkan kata yang memadai .
File dapat diproses dengan berbagai cara - beberapa di antaranya tergantung pada konten
file, beberapa pada niat programmer.

Bagaimanapun, file yang berbeda mungkin memerlukan set operasi yang berbeda, dan
berperilaku dengan cara yang berbeda.
Objek kelas yang memadai dibuat ketika Anda membuka file dan memusnahkannya pada saat
penutupan .

Di antara dua peristiwa ini, Anda bisa menggunakan objek untuk menentukan operasi apa yang
harus dilakukan pada aliran tertentu. Operasi yang Anda izinkan digunakan ditentukan oleh cara
Anda membuka file .

Secara umum, objek berasal dari salah satu kelas yang ditampilkan di sini:

Catatan: Anda tidak pernah menggunakan konstruktor untuk menghidupkan objek ini. Satu-
satunya cara Anda mendapatkannya adalah dengan memanggil fungsi yang bernama open() .

Fungsi ini menganalisis argumen yang Anda berikan, dan secara otomatis membuat objek yang
diperlukan.

Jika Anda ingin menyingkirkan objek, Anda memanggil metode bernama close() .

Doa akan memutuskan koneksi ke objek, dan file dan akan menghapus objek.
Untuk tujuan kami, kami hanya akan memperhatikan aliran yang diwakili
oleh BufferIOBasedan TextIOBaseobjek. Anda akan segera mengerti mengapa.

6.1.8.6 Processing files


Pegangan file: lanjutan
Karena jenis konten aliran, semua aliran dibagi menjadi teks dan aliran biner .
Aliran teks terstruktur dalam garis; artinya, mereka berisi karakter tipografi (huruf, angka, tanda
baca, dll.) yang disusun dalam baris (garis), seperti yang terlihat dengan mata telanjang ketika
Anda melihat isi file di editor.

File ini ditulis (atau dibaca) sebagian besar karakter demi karakter, atau baris demi baris.
Aliran biner tidak mengandung teks tetapi urutan byte dari nilai apa pun. Urutan ini dapat,
misalnya, program yang dapat dieksekusi, gambar, audio atau klip video, file database, dll.
Karena file-file ini tidak mengandung garis, membaca dan menulis berhubungan dengan
bagian-bagian data dari berbagai ukuran. Oleh karena itu data dibaca / ditulis byte demi byte,
atau blok demi blok, di mana ukuran blok biasanya berkisar dari satu ke nilai yang dipilih secara
sewenang-wenang.

Kemudian muncul masalah halus. Dalam sistem Unix / Linux, ujung baris ditandai oleh satu
karakter bernama LF(ASCII kode 10) yang ditunjuk dalam program Python sebagai \n.
Sistem operasi lain, terutama yang berasal dari sistem CP / M prasejarah (yang berlaku untuk
sistem keluarga Windows, juga) menggunakan konvensi yang berbeda: akhir baris ditandai oleh
sepasang karakter, CRdan LF(kode ASCII 13 dan 10) yang dapat dikodekan sebagai \r\n.

Ambiguitas ini dapat menyebabkan berbagai konsekuensi yang tidak menyenangkan.


Jika Anda membuat program yang bertanggung jawab untuk memproses file teks, dan itu ditulis
untuk Windows, Anda dapat mengenali ujung garis dengan menemukan \r\nkarakter, tetapi
program yang sama yang berjalan di lingkungan Unix / Linux akan sama sekali tidak berguna,
dan sebaliknya sebaliknya: program yang ditulis untuk sistem Unix / Linux mungkin tidak berguna
di Windows.

Fitur-fitur yang tidak diinginkan dari program tersebut, yang mencegah atau menghalangi
penggunaan program dalam lingkungan yang berbeda, disebut tidak mudah dibawa .
Demikian pula, sifat dari program yang memungkinkan eksekusi di lingkungan yang berbeda
disebut portabilitas . Suatu program yang diberkahi dengan sifat seperti itu disebut program
portabel .

6.1.8.7 Processing files


Pegangan file: lanjutan
Karena masalah portabilitas (dan masih) sangat serius, keputusan dibuat untuk menyelesaikan
masalah dengan cara yang tidak menarik perhatian pengembang.

Itu dilakukan di tingkat kelas, yang bertanggung jawab untuk membaca dan menulis karakter ke
dan dari aliran. Ini bekerja dengan cara berikut:
 ketika aliran terbuka dan disarankan bahwa data dalam file terkait akan diproses sebagai
teks (atau sama sekali tidak ada penasehat seperti itu), maka dialihkan ke mode teks ;
 selama membaca / menulis baris dari / ke file terkait, tidak ada yang istimewa terjadi di
lingkungan Unix, tetapi ketika operasi yang sama dilakukan di lingkungan Windows, proses
yang disebut terjemahan karakter baris baru terjadi: ketika Anda membaca baris dari file,
setiap pasangan \r\nkarakter diganti dengan satu \nkarakter, dan sebaliknya; selama
operasi penulisan, setiap \nkarakter diganti dengan sepasang \r\nkarakter;

 mekanisme ini sepenuhnya transparan untuk program, yang dapat ditulis seolah-olah itu
dimaksudkan untuk memproses file teks Unix / Linux saja; kode sumber yang dijalankan di
lingkungan Windows juga akan berfungsi dengan baik;
 ketika aliran terbuka dan disarankan untuk melakukannya, isinya diambil apa
adanya, tanpa konversi apa pun - tidak ada byte yang ditambahkan atau dihilangkan.
Membuka sungai
The pembukaan sungai dilakukan oleh fungsi yang dapat dipanggil dengan cara berikut:
stream = open(file, mode = 'r', encoding = None)

Mari kita analisa:


 nama fungsi ( open) berbicara untuk dirinya sendiri; jika pembukaan berhasil, fungsi
mengembalikan objek aliran; jika tidak, pengecualian akan muncul
(misalnya, FileNotFoundError jika file yang akan Anda baca tidak ada );
 parameter pertama dari function ( file) menentukan nama file yang akan dikaitkan
dengan stream;
 parameter kedua ( mode) menentukan mode terbuka yang digunakan untuk aliran; itu
adalah string yang diisi dengan urutan karakter, dan masing-masing dari mereka memiliki
makna tersendiri (lebih detail segera);
 parameter ketiga ( encoding) menentukan jenis penyandian (mis., UTF-8 saat bekerja
dengan file teks)
 pembukaan harus menjadi operasi pertama yang dilakukan pada streaming.
Catatan: mode dan argumen penyandian dapat dihilangkan - nilai defaultnya diasumsikan
kemudian. Mode pembuka default membaca dalam mode teks, sedangkan penyandian
default tergantung pada platform yang digunakan.
Biarkan kami hadir dengan Anda mode terbuka yang paling penting dan berguna. Siap?

6.1.8.8 Processing files


Membuka aliran: mode
r mode terbuka: baca
 aliran akan dibuka dalam mode baca ;
 file yang terkait dengan aliran harus ada dan harus dapat dibaca, jika tidak open()fungsi
tersebut menimbulkan pengecualian.

w mode terbuka: tulis


 aliran akan dibuka dalam mode tulis ;
 file yang terkait dengan aliran tidak perlu ada ; jika tidak ada akan dibuat; jika ada, itu
akan dipotong dengan panjang nol (terhapus); jika pembuatannya tidak memungkinkan
(misalnya, karena izin sistem) open()fungsi tersebut memunculkan pengecualian.

a mode terbuka: tambahkan


 aliran akan dibuka dalam mode tambahkan ;
 file yang terkait dengan aliran tidak perlu ada ; jika tidak ada, itu akan dibuat; jika ada,
kepala perekaman virtual akan ditetapkan di akhir file (konten file sebelumnya tetap tidak
tersentuh.)
r+ mode terbuka: baca dan perbarui
 aliran akan dibuka dalam mode baca dan perbarui ;
 file yang terkait dengan aliran harus ada dan harus dapat ditulisi , jika tidak open()fungsi
tersebut menimbulkan pengecualian;
 operasi baca dan tulis diizinkan untuk streaming.

w+ mode terbuka: tulis dan perbarui


 aliran akan dibuka dalam mode tulis dan perbarui ;
 file yang terkait dengan aliran tidak perlu ada ; jika tidak ada, itu akan dibuat; konten file
sebelumnya tetap tidak tersentuh;
 operasi baca dan tulis diizinkan untuk streaming.

Memilih mode teks dan biner


Jika ada huruf bdi akhir string mode itu berarti aliran harus dibuka dalam mode biner .
Jika string mode berakhir dengan huruf t, stream dibuka dalam mode teks .
Mode teks adalah perilaku default yang diasumsikan ketika tidak ada penentu mode biner / teks
yang digunakan.

Akhirnya, pembukaan file yang berhasil akan mengatur posisi file saat ini (kepala baca / tulis
virtual) sebelum byte pertama file jika mode tidaka dan setelah byte file terakhir jika mode diatur
kea .

Mode teks Mode biner Deskripsi


rt rb Baca baca

wt wb menulis
at ab menambahkan
r+t r+b baca dan perbarui
w+t w+b tulis dan perbarui

TAMBAHAN
Anda juga dapat membuka file untuk pembuatan eksklusifnya. Anda dapat melakukan ini
menggunakan xmode terbuka. Jika file sudah ada, open()fungsi akan memunculkan eksepsi.

6.1.8.9 Processing files


Membuka aliran untuk pertama kalinya
Bayangkan kita ingin mengembangkan sebuah program yang membaca konten dari file teks
bernama: C: \ Users \ User \ Desktop \ file.txt .
Bagaimana cara membuka file itu untuk dibaca? Berikut cuplikan kode yang relevan:
try: stream = open("C:\Users\User\Desktop\file.txt", "rt") # processing goes here stream.close()
except Exception as exc: print("Cannot open the file:", exc)

Apa yang terjadi di sini?


 kami membuka blok coba-kecuali karena kami ingin menangani kesalahan runtime
dengan lembut;
 kami menggunakan open()fungsi untuk mencoba membuka file yang ditentukan
(perhatikan cara kami menentukan nama file)
 mode terbuka didefinisikan sebagai teks untuk dibaca (karena teks adalah pengaturan
default , kita dapat melewatkan tstring mode)
 dalam hal keberhasilan kita mendapatkan objek dari open()fungsi dan kita
menugaskannya ke variabel aliran;
 jika open()gagal, kami menangani pengecualian mencetak informasi kesalahan penuh
(pasti baik untuk mengetahui apa yang sebenarnya terjadi)
Aliran yang sudah dibuka
Kami katakan sebelumnya bahwa setiap operasi aliran harus didahului
dengan open()pemanggilan fungsi. Ada tiga pengecualian untuk aturan tersebut.
When our program starts, the three streams are already opened and don't require any extra
preparations. What's more, your program can use these streams explicitly if you take care to
import the sys module:
import sys
because that's where the declaration of the three streams is placed.

The names of these streams are: sys.stdin, sys.stdout, and sys.stderr.


Let's analyze them:
 sys.stdin
o stdin (as standard input)
o the stdin stream is normally associated with the keyboard, pre-open for reading
and regarded as the primary data source for the running programs;
o the well-known input() function reads data from stdin by default.

 sys.stdout
o stdout (as standard output)
o the stdout stream is normally associated with the screen, pre-open for writing,
regarded as the primary target for outputting data by the running program;
o the well-known print() function outputs the data to the stdout stream.

 sys.stderr
o stderr (as standard error output)
o the stderr stream is normally associated with the screen, pre-open for writing,
regarded as the primary place where the running program should send
information on the errors encountered during its work;
o we haven't presented any method to send the data to this stream (we will do it
soon, we promise)
o pemisahan stdout(hasil berguna yang dihasilkan oleh program) dari stderr(pesan
kesalahan, tidak dapat disangkal bermanfaat tetapi tidak memberikan hasil)
memberikan kemungkinan untuk mengarahkan kedua jenis informasi ini ke target
yang berbeda. Diskusi yang lebih luas tentang masalah ini berada di luar cakupan
kursus kami. Buku pegangan sistem operasi akan memberikan lebih banyak
informasi tentang masalah-masalah ini.

6.1.8.10 Processing files


Aliran penutup
Operasi terakhir dilakukan pada sungai (ini tidak termasuk stdin, stdoutdan stderrsungai yang
tidak memerlukan itu) harus menutup .

Tindakan yang dilakukan dengan metode dipanggil dari dalam terbuka sungai
objek: stream.close().
 nama fungsi pasti komentar sendiri ( close())
 fungsi ini tidak mengharapkan argumen; aliran tidak perlu dibuka
 fungsi tidak menghasilkan apa-apa selain menimbulkan pengecualian IOError jika terjadi
kesalahan;
 sebagian besar pengembang percaya bahwa close()fungsi selalu berhasil dan dengan
demikian tidak perlu memeriksa apakah itu dilakukan dengan benar.

Keyakinan ini hanya dibenarkan sebagian. Jika aliran dibuka untuk penulisan dan
kemudian serangkaian operasi penulisan dilakukan, mungkin saja data yang dikirim ke
aliran belum ditransfer ke perangkat fisik (karena mekanisme yang
disebut caching atau buffering ). Karena penutupan sungai memaksa buffer untuk
menyiramnya, mungkin flushes gagal dan karenanya close()gagal juga.

Kami telah menyebutkan kegagalan yang disebabkan oleh fungsi yang beroperasi dengan
stream tetapi tidak menyebutkan sepatah kata pun bagaimana tepatnya kami dapat
mengidentifikasi penyebab kegagalan tersebut.

Kemungkinan membuat diagnosis ada dan disediakan oleh salah satu komponen pengecualian
stream yang akan kami sampaikan kepada Anda baru saja.
Mendiagnosis masalah aliran

The IOErrorobjek dilengkapi dengan properti bernama errno(nama berasal dari frase nomor
kesalahan ) dan Anda dapat mengaksesnya sebagai berikut:
try: # some stream operations except IOError as exc: print(exc.errno)

Nilai errnoatribut dapat dibandingkan dengan salah satu konstanta simbolis yang telah
ditentukan yang didefinisikan dalam errnomodul.

Mari kita lihat beberapa konstanta terpilih yang berguna untuk mendeteksi kesalahan aliran :
errno.EACCES→ Izin ditolak

Kesalahan terjadi ketika Anda mencoba, misalnya, untuk membuka file dengan atribut hanya
baca untuk menulis.

errno.EBADF→ Nomor file salah


Kesalahan terjadi ketika Anda mencoba, misalnya, untuk beroperasi dengan aliran yang belum
dibuka.
errno.EEXIST→ File ada
Kesalahan terjadi ketika Anda mencoba, misalnya, untuk mengganti nama file dengan nama
sebelumnya.

errno.EFBIG→ File terlalu besar


Kesalahan terjadi ketika Anda mencoba membuat file yang lebih besar dari maksimum yang
diizinkan oleh sistem operasi.

errno.EISDIR→ Adalah direktori


Kesalahan terjadi ketika Anda mencoba memperlakukan nama direktori sebagai nama file
biasa.

errno.EMFILE→ Terlalu banyak file yang terbuka


Kesalahan terjadi ketika Anda mencoba untuk secara bersamaan membuka lebih banyak aliran
daripada yang dapat diterima untuk sistem operasi Anda.

errno.ENOENT→ Tidak ada file atau direktori tersebut


Kesalahan terjadi ketika Anda mencoba mengakses file / direktori yang tidak ada.

errno.ENOSPC→ Tidak ada ruang yang tersisa di perangkat


Kesalahan terjadi ketika tidak ada ruang kosong di media.

Daftar lengkapnya jauh lebih lama (termasuk juga beberapa kode kesalahan yang tidak terkait
dengan pemrosesan aliran.)

6.1.8.11 Processing files


Mendiagnosis masalah aliran: lanjutan
Jika Anda seorang programmer yang sangat hati-hati, Anda mungkin merasa perlu
menggunakan urutan pernyataan yang serupa dengan yang disajikan di bawah ini:
import errno try: s = open("c:/users/user/Desktop/file.txt", "rt") # actual processing goes here
s.close() except Exception as exc: if exc.errno == errno.ENOENT: print("The file doesn't exist.") elif
exc.errno == errno.EMFILE: print("You've opened too many files.") else: printf("The error number is:",
exc.errno)

Untungnya, ada fungsi yang secara dramatis dapat menyederhanakan kode penanganan
kesalahan . Namanya strerror(), dan itu berasal dari osmodul dan mengharapkan hanya satu
argumen - nomor kesalahan .
Perannya sederhana: Anda memberikan nomor kesalahan dan mendapatkan string yang
menjelaskan arti dari kesalahan tersebut.

Catatan: jika Anda melewatkan kode kesalahan yang tidak ada (angka yang tidak terikat
dengan kesalahan aktual), fungsi tersebut akan meningkatkan pengecualian ValueError .
Sekarang kita dapat menyederhanakan kode kita dengan cara berikut:
from os import strerror try: s = open("c:/users/user/Desktop/file.txt", "rt") # actual processing goes
here s.close() except Exception as exc: print("The file could not be opened:", strerror(exc.errno));

Baik. Sekarang saatnya berurusan dengan file teks dan mengenal beberapa teknik dasar yang
dapat Anda gunakan untuk memprosesnya.

6.1.9.1 Working with real files


Memproses file teks
Dalam pelajaran ini kita akan menyiapkan file teks sederhana dengan beberapa konten pendek
dan sederhana.

Kami akan menunjukkan kepada Anda beberapa teknik dasar yang dapat Anda manfaatkan
untuk membaca konten fileuntuk memprosesnya.

Pemrosesannya akan sangat sederhana - Anda akan menyalin konten file ke konsol, dan
menghitung semua karakter yang telah dibaca oleh program.

Tapi ingat - pemahaman kita tentang file teks sangat ketat. Dalam pengertian kami, ini adalah
file teks biasa - mungkin hanya berisi teks, tanpa dekorasi tambahan (pemformatan, font yang
berbeda, dll.).

Itu sebabnya Anda harus menghindari membuat file menggunakan prosesor teks canggih seperti
MS Word, LibreOffice Writer, atau sesuatu seperti ini. Gunakan dasar-dasar yang ditawarkan OS
Anda: Notepad, vim, gedit, dll.

Jika file teks Anda mengandung beberapa karakter nasional yang tidak tercakup oleh rangkaian
karakter ASCII standar, Anda mungkin perlu langkah tambahan. open()Doa
fungsi Anda mungkin memerlukan argumen yang menunjukkan pengkodean teks tertentu.

Misalnya, jika Anda menggunakan OS Unix / Linux yang dikonfigurasikan untuk menggunakan
UTF-8 sebagai pengaturan seluruh sistem, open()fungsi tersebut mungkin terlihat sebagai berikut:
stream = open('file.txt', 'rt', encoding='utf-8')

di mana argumen pengkodean harus diatur ke nilai yang merupakan string yang mewakili
pengkodean teks yang tepat (UTF-8, di sini).

Baca dokumentasi OS Anda untuk menemukan nama penyandian yang memadai untuk
lingkungan Anda.

INFORMASI
Untuk keperluan percobaan kami dengan pemrosesan file yang dilakukan di bagian ini, kami
akan menggunakan sekumpulan file yang diunggah (mis., File tzop.txt, atau text.txt ) yang dapat
Anda gunakan untuk bekerja . Jika Anda ingin bekerja dengan file Anda sendiri secara lokal di
mesin Anda, kami sangat menganjurkan Anda untuk melakukannya, dan menggunakan IDLE
untuk melakukan tes Anda sendiri.

6.1.9.2 Working with real files


Memproses file teks: lanjutan
Membaca konten file teks dapat dilakukan dengan menggunakan beberapa metode berbeda
- tidak ada yang lebih baik atau lebih buruk daripada yang lain. Terserah Anda yang mana yang
Anda sukai dan sukai.
Beberapa dari mereka terkadang akan lebih handier, dan kadang-kadang lebih
merepotkan. Jadilah fleksibel. Jangan takut untuk mengubah preferensi Anda.
Yang paling dasar dari metode ini adalah yang ditawarkan oleh read()fungsi, yang dapat Anda
lihat beraksi dalam pelajaran sebelumnya.

Jika diterapkan ke file teks, fungsi ini dapat:


 baca sejumlah karakter yang diinginkan (termasuk hanya satu) dari file, dan kembalikan
sebagai string;
 baca semua isi file, dan kembalikan sebagai string;
 jika tidak ada lagi untuk dibaca (kepala pembacaan virtual mencapai akhir file), fungsi
mengembalikan string kosong.

Kami akan mulai dengan varian paling sederhana dan menggunakan file bernama text.txt. File
memiliki konten berikut:
Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex.
Complex is better than complicated.

Sekarang lihat kode di editor, dan mari kita menganalisisnya.


Rutinitasnya agak sederhana:
 gunakan mekanisme coba-kecuali dan buka file dari nama yang telah ditentukan
( text.txt dalam kasus kami)
 coba baca karakter pertama dari file ( ch = s.read(1))
 jika Anda berhasil (ini dibuktikan dengan hasil positif dari whilepemeriksaan kondisi),
keluarkan karakter (perhatikan end=argumen - penting! Anda tidak ingin melompat ke
baris baru setelah setiap karakter!);
 perbarui penghitung ( cnt), juga;
 coba baca karakter selanjutnya, dan prosesnya berulang.

6.1.9.3 Working with real files


Memproses file teks: lanjutan
Jika Anda benar-benar yakin bahwa panjang file aman dan Anda dapat membaca seluruh file
ke memori sekaligus, Anda dapat melakukannya - read()fungsinya, dipanggil tanpa argumen
atau dengan argumen yang mengevaluasi None, akan melakukan pekerjaan untuk kamu.

Ingat - membaca file terabyte-panjang menggunakan metode ini dapat merusak OS Anda .
Jangan berharap keajaiban - memori komputer tidak bisa ditarik.

Lihatlah kode di editor. Apa yang kamu pikirkan tentang itu?

Mari kita analisa:


 buka file seperti sebelumnya;
 baca isinya dengan satu read()fungsi doa;
 selanjutnya, proses teks, iterasi melalui itu dengan forloop reguler , dan memperbarui nilai
penghitung pada setiap putaran loop;
Hasilnya akan persis sama seperti sebelumnya.

6.1.9.4 Working with real files


Memproses file teks: readline ()
Jika Anda ingin memperlakukan konten file sebagai kumpulan garis , bukan sekelompok
karakter, readline()metode ini akan membantu Anda.

Metode ini mencoba membaca baris teks lengkap dari file , dan mengembalikannya sebagai
string jika berhasil. Jika tidak, ia mengembalikan string kosong.
Ini membuka peluang baru - sekarang Anda juga dapat menghitung garis dengan mudah, tidak
hanya karakter.
Mari kita manfaatkan itu. Lihatlah kode di editor.
Seperti yang dapat Anda lihat, ide umumnya sama persis seperti pada kedua contoh
sebelumnya.

6.1.9.5 Working with real files


Memproses file teks: readlines ()
Metode lain, yang memperlakukan file teks sebagai kumpulan garis, bukan karakter,
adalah readlines().

The readlines()metode, ketika dijalankan tanpa argumen, mencoba untuk membaca semua isi
file, dan mengembalikan daftar string, satu elemen per baris file yang .
Jika Anda tidak yakin apakah ukuran file cukup kecil dan tidak ingin menguji OS, Anda dapat
meyakinkan readlines()metode untuk membaca tidak lebih dari jumlah byte yang ditentukan
sekaligus (nilai pengembalian tetap sama - itu adalah daftar string).
Silakan bereksperimen dengan kode contoh ini untuk memahami cara kerja readlines()metode
ini.

Ukuran buffer input maksimum yang diterima dilewatkan ke metode sebagai argumennya .
Anda mungkin berharap bahwa readlines()dapat memproses konten file lebih efektif
daripada readline(), karena mungkin perlu dipanggil lebih sedikit.

Catatan: ketika tidak ada yang dapat dibaca dari file, metode mengembalikan daftar
kosong. Gunakan itu untuk mendeteksi akhir file.
Sejauh ukuran buffer, Anda dapat berharap bahwa meningkatkannya dapat meningkatkan
kinerja input, tetapi tidak ada aturan emas untuk itu - cobalah untuk menemukan sendiri nilai
optimalnya.

Lihatlah kode di editor. Kami telah memodifikasinya untuk menunjukkan kepada Anda cara
menggunakan readlines().

Kami telah memutuskan untuk menggunakan buffer sepanjang 15 byte. Jangan pikir itu
rekomendasi.
Kami telah menggunakan nilai seperti itu untuk menghindari situasi di
mana readlines()doa pertama menghabiskan seluruh file.

Kami ingin metode ini dipaksa untuk bekerja lebih keras, dan menunjukkan kemampuannya.
Ada dua loop bersarang dalam kode : yang luar menggunakan readlines()hasil untuk beralih
melalui itu, sedangkan yang dalam mencetak karakter garis dengan karakter.

6.1.9.6 Working with real files


Memproses file teks: lanjutan
Contoh terakhir yang ingin kami sajikan menunjukkan sifat yang sangat menarik dari objek yang
dikembalikan oleh open()fungsi dalam mode teks.

Kami pikir itu mungkin mengejutkan Anda - objek adalah turunan dari kelas iterable .
Aneh? Tidak semuanya. Dapat digunakan? Ya, tentu saja.
The protokol iterasi yang ditetapkan untuk file objek sangat sederhana - nya __next__metode
hanya mengembalikan baris berikutnya dibaca dari file .

Selain itu, Anda dapat berharap bahwa objek secara otomatis memanggil close()ketika salah
satu file yang dibaca mencapai akhir file.
Lihatlah editor dan lihat betapa sederhananya dan jelas kodenya sekarang.

6.1.9.7 Working with real files


Berurusan dengan file teks: write ()
Menulis file teks tampaknya lebih sederhana, karena sebenarnya ada satu metode yang dapat
digunakan untuk melakukan tugas seperti itu.

p> Metode ini dinamai write()dan mengharapkan hanya satu argumen - string yang akan
ditransfer ke file terbuka (jangan lupa - mode terbuka harus mencerminkan cara transfer data
- menulis file dibuka dalam mode baca tidak akan berhasil ).

Tidak ada karakter baris baru ditambahkan ke write()argumen 's, jadi Anda harus
menambahkannya sendiri jika Anda ingin file diisi dengan sejumlah baris.

Contoh di editor menunjukkan kode yang sangat sederhana yang membuat file
bernama newtext.txt (catatan: mode terbuka wmemastikan bahwa file akan dibuat dari awal ,
bahkan jika ada dan berisi data) dan kemudian menempatkan sepuluh baris ke dalamnya.
String yang akan direkam terdiri dari baris kata, diikuti oleh nomor baris. Kami telah memutuskan
untuk menulis karakter isi string dengan karakter (ini dilakukan oleh forlingkaran dalam ) tetapi
Anda tidak wajib melakukannya dengan cara ini.

Kami hanya ingin menunjukkan kepada Anda yang write()dapat beroperasi pada satu karakter.
Kode menciptakan file yang diisi dengan teks berikut:
line #1 line #2 line #3 line #4 line #5 line #6 line #7 line #8 line #9 line #10

Kami mendorong Anda untuk menguji perilaku write()metode ini secara lokal di mesin Anda.

6.1.9.8 Working with real files


Berurusan dengan file teks: lanjutan
Lihatlah contoh di editor. Kami telah memodifikasi kode sebelumnya untuk menulis seluruh baris
ke file teks.
Isi file yang baru dibuat adalah sama.

Catatan: Anda dapat menggunakan metode yang sama untuk menulis ke stderraliran, tetapi
jangan mencoba membukanya, karena selalu terbuka secara implisit.

Misalnya, jika Anda ingin mengirim string pesan stderruntuk membedakannya dari output
program normal, itu mungkin terlihat seperti ini:
import sys sys.stderr.write("Error message")

6.1.9.9 Working with real files


Apa itu bytearray?
Sebelum kita mulai berbicara tentang file biner, kita harus memberi tahu Anda tentang salah
satu kelas khusus yang digunakan Python untuk menyimpan data tidak berbentuk .

Data amorf adalah data yang tidak memiliki bentuk atau bentuk spesifik - mereka hanya
serangkaian byte.
Ini tidak berarti bahwa byte ini tidak dapat memiliki artinya sendiri, atau tidak dapat mewakili
objek yang berguna, misalnya, grafik bitmap.

Aspek yang paling penting dari ini adalah bahwa di tempat di mana kami memiliki kontak
dengan data, kami tidak dapat, atau tidak ingin, tahu apa-apa tentang itu.
Data amorf tidak dapat disimpan menggunakan cara apa pun yang disajikan sebelumnya -
mereka bukan string atau daftar.
Seharusnya ada wadah khusus yang bisa menangani data tersebut.

Python memiliki lebih dari satu wadah seperti itu - salah satunya adalah nama kelas khusus
bytearray - seperti namanya, ini adalah array yang berisi byte (amorf) .
Jika Anda ingin memiliki wadah seperti itu, misalnya, untuk membaca gambar bitmap dan
memprosesnya dengan cara apa pun, Anda harus membuatnya secara eksplisit, menggunakan
salah satu konstruktor yang tersedia.
Lihatlah:
data = bytearray(100)

Doa semacam itu menciptakan objek bytearray yang mampu menyimpan sepuluh byte.
Catatan: konstruktor seperti itu mengisi seluruh array dengan nol .

6.1.9.10 Working with real files


Bytearrays: lanjutan
Bytearrays menyerupai daftar dalam banyak hal. Misalnya, mereka bisa berubah , mereka
adalah subjek len()fungsi, dan Anda dapat mengakses elemen mereka menggunakan
pengindeksan konvensional.

Ada satu batasan penting - Anda tidak boleh mengatur elemen array byte apa pun dengan nilai
yang bukan bilangan bulat (melanggar aturan ini akan menyebabkan pengecualian TypeError )
dan Anda tidak diizinkan untuk menetapkan nilai yang tidak berasal dari kisaran 0 hingga 255
inklusif (kecuali jika Anda ingin memprovokasi pengecualian ValueError ).
Anda dapat memperlakukan elemen array byte apa pun sebagai nilai integer - seperti dalam
contoh di editor.

Catatan: kami telah menggunakan dua metode untuk mengulangi byte array, dan
memanfaatkan hex()fungsi tersebut untuk melihat elemen yang dicetak sebagai nilai
heksadesimal.

Sekarang kami akan menunjukkan kepada Anda bagaimana menulis array byte ke file biner -
biner, karena kami tidak ingin menyimpan representasi yang dapat dibaca - kami ingin menulis
salinan konten memori fisik satu-ke-satu, byte demi byte.

6.1.9.11 Working with real files


Bytearrays: lanjutan
Jadi, bagaimana kita menulis array byte ke file biner?
Lihatlah kode di editor. Mari kita analisa:
 pertama, kami menginisialisasi bytearraydengan nilai selanjutnya mulai dari 10; jika Anda
ingin konten file dapat dibaca dengan jelas, ganti 10dengan sesuatu seperti ord('a')- ini
akan menghasilkan byte yang berisi nilai yang sesuai dengan bagian alfabet kode ASCII
(jangan berpikir itu akan membuat file file teks - masih biner, karena itu dibuat
dengan wbbendera);
 kemudian, kami membuat file menggunakan open()fungsi - satu-satunya perbedaan
dibandingkan dengan varian sebelumnya adalah mode terbuka yang
mengandung bflag;
 yang write()metode mengambil argumen ( bytearray) dan mengirimkannya (secara
keseluruhan) ke file;
 aliran kemudian ditutup secara rutin.

The write()Metode mengembalikan jumlah byte yang berhasil ditulis.

Jika nilainya berbeda dari panjang argumen metode, itu mungkin mengumumkan beberapa
kesalahan tulis.

Dalam hal ini, kami belum memanfaatkan hasilnya - ini mungkin tidak sesuai dalam setiap kasus.
Cobalah untuk menjalankan kode dan menganalisis konten file output yang baru dibuat.
Anda akan menggunakannya di langkah berikutnya.

Cara membaca byte dari aliran


Membaca dari file biner membutuhkan penggunaan nama metode khusus readinto(), karena
metode ini tidak membuat objek array byte baru, tetapi mengisi yang dibuat sebelumnya
dengan nilai yang diambil dari file biner.
catatan:
 metode mengembalikan jumlah byte yang berhasil dibaca;
 metode mencoba untuk mengisi seluruh ruang yang tersedia di dalam argumennya; jika
ada lebih banyak data dalam file daripada ruang dalam argumen, operasi baca akan
berhenti sebelum akhir file; jika tidak, hasil metode dapat menunjukkan bahwa array byte
hanya diisi secara terpisah (hasilnya akan menunjukkan kepada Anda bahwa juga, dan
bagian array yang tidak digunakan oleh konten yang baru dibaca tetap tidak tersentuh)

Lihat kode lengkap di bawah ini:


from os import strerror data = bytearray(10) try: bf = open('file.bin', 'rb') bf.readinto(data) bf.close()
for b in data: print(hex(b), end=' ') except IOError as e: print("I/O error occurred:", strerr(e.errno))

Mari kita analisa:


 pertama, kami membuka file (yang Anda buat menggunakan kode sebelumnya)
dengan mode yang digambarkan sebagai rb;
 kemudian, kita membaca isinya ke dalam array byte bernama data, berukuran sepuluh
byte;
 akhirnya, kita mencetak isi array byte - apakah sama dengan yang Anda harapkan?
Jalankan kode dan periksa apakah itu berfungsi.

6.1.9.12 Working with real files


Cara membaca byte dari aliran
Cara alternatif membaca konten file biner ditawarkan oleh metode yang dinamai read().
Dipanggil tanpa argumen, ia mencoba membaca semua isi file ke dalam memori ,
menjadikannya bagian dari objek yang baru dibuat dari kelas byte.

Kelas ini memiliki beberapa kesamaan bytearray, dengan pengecualian satu perbedaan
signifikan - tidak dapat diubah .

Untungnya, tidak ada kendala untuk membuat array byte dengan mengambil nilai awalnya
langsung dari objek byte, seperti di sini:
from os import strerror try: bf = open('file.bin', 'rb') data = bytearray(bf.read()) bf.close() for b in
data: print(hex(b), end=' ') except IOError as e: print("I/O error occurred:", strerr(e.errno))

Hati-hati - jangan gunakan pembacaan seperti ini jika Anda tidak yakin bahwa isi file akan sesuai
dengan memori yang tersedia .

6.1.9.13 Working with real files


Cara membaca byte dari aliran: lanjutan
Jika read()metode ini dipanggil dengan argumen, itu menentukan jumlah maksimum byte untuk
dibaca .

Metode ini mencoba membaca jumlah byte yang diinginkan dari file, dan panjang objek yang
dikembalikan dapat digunakan untuk menentukan jumlah byte yang benar-benar dibaca.
Anda dapat menggunakan metode ini seperti di sini:
try: bf = open('file.bin', 'rb') data = bytearray(bf.read(5)) bf.close() for b in data: print(hex(b),
end=' ') except IOError as e: print("I/O error occurred:", strerr(e.errno))

Catatan: lima byte pertama file telah dibaca oleh kode - lima berikutnya masih menunggu untuk
diproses.

6.1.9.14 Working with real files


Menyalin file - alat yang sederhana dan fungsional
Sekarang Anda akan menggabungkan semua pengetahuan baru ini, menambahkan beberapa
elemen baru ke dalamnya, dan menggunakannya untuk menulis kode nyata yang benar-benar
dapat menyalin isi file.
Tentu saja, tujuannya bukan untuk membuat penggantian yang lebih baik untuk perintah
seperti salin (MS Windows) atau cp (Unix / Linux) tetapi untuk melihat satu cara yang mungkin
untuk membuat alat yang berfungsi, bahkan jika tidak ada yang ingin menggunakannya.

Lihatlah kode di editor. Mari kita analisa:


 baris 3 hingga 8: minta pengguna untuk menyalin nama file, dan coba buka untuk
dibaca; menghentikan eksekusi program jika pembukaan gagal; catatan:
gunakan exit()fungsi untuk menghentikan eksekusi program dan untuk meneruskan kode
penyelesaian ke OS; kode penyelesaian apa pun selain yang 0mengatakan bahwa
program telah mengalami beberapa masalah; gunakan errnonilai untuk menentukan
sifat masalah;
 baris 9 hingga 15: ulangi tindakan yang hampir sama, tetapi kali ini untuk file output;
 baris 17: menyiapkan sepotong memori untuk mentransfer data dari file sumber ke file
target; area transfer seperti itu sering disebut buffer, maka nama variabel; ukuran buffer
berubah-ubah - dalam hal ini, kami memutuskan untuk menggunakan 64 kilobyte; secara
teknis, buffer yang lebih besar lebih cepat dalam menyalin item, karena buffer yang lebih
besar berarti lebih sedikit operasi I / O; sebenarnya, selalu ada batas, persimpangan yang
tidak membuat perbaikan lebih lanjut; ujilah sendiri jika Anda mau.
 baris 18: hitung byte yang disalin - ini adalah penghitung dan nilai awalnya;
 baris 20: coba isi buffer untuk pertama kali;
 baris 21: selama Anda mendapatkan jumlah byte yang tidak nol, ulangi tindakan yang
sama;
 baris 22: tulis konten buffer ke file output (catatan: kami telah menggunakan slice untuk
membatasi jumlah byte yang ditulis, karena write()selalu lebih suka menulis seluruh buffer)
 baris 23: perbarui penghitung;
 baris 24: baca potongan file selanjutnya;
 baris 29 hingga 31: pembersihan akhir - pekerjaan selesai.

6.1.9.15 LAB: Character frequency histogram Lab


LABORATORIUM

Perkiraan waktu
30 menit
Tingkat kesulitan
Medium
Tujuan
 meningkatkan keterampilan siswa dalam mengoperasikan file (membaca)
 menggunakan koleksi data untuk menghitung banyak data.
Skenario
File teks berisi beberapa teks (tidak ada yang tidak biasa) tetapi kita perlu tahu seberapa sering
(atau seberapa jarang) setiap huruf muncul dalam teks. Analisis semacam itu mungkin berguna
dalam kriptografi, jadi kami ingin dapat melakukannya dengan mengacu pada alfabet Latin.
Tugas Anda adalah menulis sebuah program yang:
 meminta pengguna untuk nama file input;
 membaca file (jika mungkin) dan menghitung semua huruf Latin (huruf kecil dan huruf
besar diperlakukan sama)
 mencetak histogram sederhana dalam urutan abjad (hanya hitungan non-nol yang
disajikan)
Buat file uji untuk kode tersebut, dan periksa apakah histogram Anda berisi hasil yang valid.
Dengan asumsi bahwa file uji hanya berisi satu baris diisi dengan:
aBc

output yang diharapkan akan terlihat sebagai berikut:a -> 1 b -> 1 c -> 1
Kiat :
Kami berpikir bahwa kamus adalah media pengumpulan data yang sempurna untuk
menyimpan jumlah. Huruf-hurufnya mungkin merupakan kunci sementara penghitung bisa
berupa nilai.
6.1.9.16 LAB: Sorted character frequency histogram Lab
LABORATORIUM

Perkiraan waktu
15-20 menit
Tingkat kesulitan
Medium
Prasyarat
05_9.15.1
Tujuan
 meningkatkan keterampilan siswa dalam mengoperasikan file (membaca / menulis)
 menggunakan lambdas untuk mengubah urutan.
Skenario
Kode sebelumnya perlu ditingkatkan. Tidak apa-apa, tapi itu harus lebih baik.
Tugas Anda adalah membuat beberapa amandemen, yang menghasilkan hasil berikut:
 histogram output akan diurutkan berdasarkan frekuensi karakter (penghitung yang lebih
besar harus disajikan terlebih dahulu)
 histogram harus dikirim ke file dengan nama yang sama dengan input, tetapi dengan
akhiran '.hist' (harus digabung dengan nama asli)
Dengan asumsi bahwa file input hanya berisi satu baris diisi dengan:
cBabAa

output yang diharapkan akan terlihat sebagai berikut:a -> 3 b -> 2 c -> 1
Kiat :
Gunakan a lambdauntuk mengubah urutan pengurutan.

6.1.9.17 LAB: Evaluating students' results Lab


LABORATORIUM

Perkiraan waktu
30 menit
Tingkat kesulitan
Medium
Tujuan
 meningkatkan keterampilan siswa dalam mengoperasikan file (membaca)
 menyempurnakan kemampuan siswa dalam mendefinisikan dan menggunakan
pengecualian dan kamus yang ditentukan sendiri.
Skenario
Prof. Jekyll mengadakan kelas bersama siswa dan secara teratur membuat catatan dalam file
teks. Setiap baris file berisi 3 elemen: nama depan siswa, nama belakang siswa, dan jumlah poin
yang diterima siswa selama kelas-kelas tertentu.
Elemen-elemen dipisahkan dengan ruang putih. Setiap siswa dapat muncul lebih dari satu kali di
dalam file Prof. Jekyll.
File tersebut mungkin terlihat sebagai berikut:
John Smith 5 Anna Boleyn 4.5 John Smith 2 Anna Boleyn 11 Andrew Cox 1.5

Tugas Anda adalah menulis sebuah program yang:


 meminta pengguna untuk nama file Prof. Jekyll;
 membaca isi file dan menghitung jumlah poin yang diterima untuk setiap siswa;
 mencetak laporan sederhana (tetapi diurutkan), seperti yang ini:
Andrew Cox 1.5 Anna Boleyn 15.5 John Smith 7.0

catatan:
 program Anda harus sepenuhnya dilindungi dari semua kemungkinan kegagalan: file
tidak ada, kekosongan file, atau kegagalan data input apa pun; menghadapi kesalahan
data apa pun harus menyebabkan penghentian program segera, dan kesalahan harus
disajikan kepada pengguna;
 menerapkan dan menggunakan hierarki pengecualian Anda sendiri - kami telah
menyajikannya di editor; pengecualian kedua harus dimunculkan ketika baris yang buruk
terdeteksi, dan yang ketiga ketika file sumber ada tetapi kosong.
Kiat :
Gunakan kamus untuk menyimpan data siswa.

Anda mungkin juga menyukai