MODUL 6

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-D

Views 193 Downloads 0 File size 1018KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

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 yang lebih terspesialisasi. Kelas khusus menjadi superclass bagi mereka semua.

harus mendefinisikan beberapa kelas adalah subclass. Kelas kendaraan akan

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 siasia. 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 status selfparameter akan mencerminkan objek yang baru dibuat.

yang

memodifikasi

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 argumenmemohon 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 instanmenjadi pribadi - menjadi tidak dapat diakses dari dunia luar.

membuat

variabel

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 menyebabkan pengecualian AttributeError .

atribut

objek

(kelas)

yang

tidak

ada

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 dan False sebaliknya. Bisakah Anda menebak output kode? Jalankan untuk memeriksa tebakan Anda.

diberikan,

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:

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 sementara str1dan str2tidak, meskipun isinya sama.

benda

yang

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 dimaksudkan untuk menjadi bermakna).

ini

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 aturanaturan 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 dari returnpernyataan itu, dengan satu perbedaan penting. Lihatlah fungsi ini: def fun(n): for i in range(n): return i

saudara

yang

lebih

cerdas

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 dan expression_twosebaliknya.

sama

dengan expression_one ketika

kondisinya True,

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. Satusatunya 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 oleh BufferIOBasedan TextIOBaseobjek. Anda akan segera mengerti mengapa.

diwakili

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 menambahkannya sendiri jika Anda ingin file diisi dengan sejumlah baris.

Anda

harus

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.