Menulis modul Anda sendiri tidak berbeda jauh dari menulis skrip biasa.
Ada beberapa aspek khusus yang harus Anda ketahui, tetapi itu pasti tidaklah sulit. Anda akan segera melihatnya.
Mari kita rangkum beberapa isu penting:
Sebuah modul adalah jenis wadah yang diisi dengan fungsi - Anda dapat memasukkan sebanyak fungsi yang Anda inginkan ke dalam satu modul dan mendistribusikannya ke seluruh dunia;
tentu saja, secara umum adalah ide yang baik untuk tidak mencampurkan fungsi dengan area aplikasi yang berbeda dalam satu modul (seperti di perpustakaan - tidak ada yang mengharapkan karya ilmiah ditempatkan di antara buku komik), jadi kelompokkan fungsi Anda dengan hati-hati dan beri nama modul yang berisi fungsi tersebut dengan cara yang jelas dan intuitif (misalnya, jangan beri nama arcade_games untuk modul yang berisi fungsi yang dimaksudkan untuk mempartisi dan memformat hard disk)
membuat banyak modul dapat menimbulkan sedikit kekacauan - suatu saat nanti Anda akan ingin mengelompokkan modul Anda dengan cara yang sama seperti saat Anda mengelompokkan fungsi - apakah ada wadah yang lebih umum dari sebuah modul?
ya, ada - itu adalah sebuah paket; di dunia modul, sebuah paket memainkan peran yang serupa dengan folder/direktori di dunia file.
Pada bagian ini, Anda akan bekerja secara lokal pada mesin Anda. Mari kita mulai dari awal. Buatlah file kosong seperti ini:
Anda akan memerlukan dua file untuk mengulang eksperimen ini. Yang pertama adalah modul itu sendiri. Saat ini kosong. Jangan khawatir, Anda akan segera mengisinya dengan kode aktual.
Kami menamai file tersebut module.py. Tidak terlalu kreatif, tapi sederhana dan jelas.
File kedua berisi kode menggunakan modul baru tersebut. Namanya adalah main.py. Isinya sangat singkat sampai saat ini:
Catatan: kedua file harus berada di folder yang sama. Kami sangat menyarankan Anda untuk membuat folder kosong baru untuk kedua file tersebut. Beberapa hal akan lebih mudah nanti.
Buka IDLE (atau IDE lain yang Anda pilih) dan jalankan file main.py. Apa yang kamu lihat?
Anda seharusnya tidak melihat apa-apa. Ini berarti bahwa Python telah berhasil mengimpor konten dari file module.py.
Tidak masalah bahwa modulnya kosong untuk saat ini. Langkah pertama telah dilakukan, tetapi sebelum Anda mengambil langkah berikutnya, kami ingin Anda melihat ke dalam folder di mana kedua file berada.
Apakah Anda melihat sesuatu yang menarik?
Sebuah subfolder baru muncul - bisa kamu lihat? Namanya adalah pycache. Lihat ke dalamnya. Apa yang kamu lihat?
Ada sebuah file yang bernama (kurang lebih) module.cpython-xy.pyc di mana x dan y adalah digit yang berasal dari versi Python Anda (misalnya, mereka akan menjadi 3 dan 8 jika Anda menggunakan Python 3.8).
Nama file tersebut sama dengan nama modul Anda (di sini adalah module). Bagian setelah titik pertama memberitahu siapa yang membuat file (di sini adalah CPython) dan nomor versinya. Bagian terakhir (pyc) berasal dari kata Python dan terkompilasi.
Anda dapat melihat ke dalam file tersebut - kontennya sepenuhnya tidak dapat dibaca oleh manusia. Itu harus seperti itu, karena file tersebut ditujukan hanya untuk digunakan oleh Python.
Ketika Python mengimpor sebuah modul untuk pertama kalinya, ia menerjemahkan isinya menjadi bentuk yang agak terkompilasi.
File tersebut tidak berisi kode mesin - itu adalah kode semi-terkompilasi internal Python, siap untuk dieksekusi oleh interpreter Python. Karena file seperti itu tidak memerlukan banyak pemeriksaan yang diperlukan untuk file sumber murni, eksekusi dimulai lebih cepat dan berjalan lebih cepat juga.
Berkat itu, setiap pengimporan berikutnya akan berjalan lebih cepat daripada menginterpretasikan teks sumber dari awal.
Python mampu memeriksa apakah file sumber modul telah dimodifikasi (dalam hal ini, file pyc akan dibangun kembali) atau tidak (ketika file pyc dapat dijalankan sekaligus). Karena proses ini sepenuhnya otomatis dan transparan, Anda tidak perlu memikirkannya.
Sekarang kita telah menambahkan sedikit sesuatu ke dalam file modul:
Apakah kamu dapat melihat perbedaan antara modul dan skrip biasa? Sampai saat ini belum ada.
File ini dapat dijalankan seperti skrip lainnya. Cobalah sendiri.
Apa yang terjadi? Kamu seharusnya melihat baris berikut di dalam konsol kamu:
Mari kembali ke file main.py:
Jalankan itu. Apa yang Anda lihat? Semoga Anda melihat sesuatu seperti ini:
Apa yang sebenarnya dimaksud?
Ketika sebuah modul diimpor, isinya dieksekusi secara implisit oleh Python. Hal ini memberikan kesempatan bagi modul untuk menginisialisasi beberapa aspek internalnya (misalnya, modul dapat menetapkan beberapa variabel dengan nilai-nilai yang berguna).
Catatan: inisialisasi hanya terjadi sekali, saat impor pertama kali terjadi, sehingga penugasan yang dilakukan oleh modul tidak diulang secara tidak perlu.
Bayangkan konteks berikut:
ada sebuah modul bernama mod1;
ada sebuah modul bernama mod2 yang berisi instruksi import mod1;
ada sebuah file utama yang berisi instruksi import mod1 dan import mod2.
Pada pandangan pertama, Anda mungkin berpikir bahwa mod1 akan diimpor dua kali - untungnya, hanya impor pertama yang terjadi. Python mengingat modul yang diimpor dan diam-diam mengabaikan semua impor selanjutnya.
Python dapat melakukan banyak hal. Ini juga menciptakan variabel yang disebut __name__.
Selain itu, setiap file sumber menggunakan versi variabel yang terpisah - ini tidak dibagikan antara modul.
Kami akan menunjukkan cara menggunakannya. Modifikasi modul sedikit:
Sekarang jalankan file module.py. Anda seharusnya melihat baris-baris berikut ini:
Sekarang jalankan file main.py. Dan? Apakah kamu melihat hal yang sama seperti kami?
Kita dapat mengatakan bahwa:
ketika Anda menjalankan sebuah file langsung, variabel __name__ nya diatur menjadi __main__;
ketika sebuah file diimpor sebagai sebuah modul, variabel __name__ nya diatur menjadi nama file tersebut (tidak termasuk .py).
Ini adalah cara Anda dapat memanfaatkan variabel __main__ untuk mendeteksi konteks di mana kode Anda telah diaktifkan:
Ada cara yang lebih pintar untuk memanfaatkan variabel tersebut. Jika Anda menulis sebuah modul yang berisi beberapa fungsi kompleks, Anda dapat menggunakannya untuk menempatkan serangkaian tes untuk memeriksa apakah fungsi-fungsi tersebut bekerja dengan baik.
Setiap kali Anda memodifikasi salah satu dari fungsi-fungsi ini, Anda dapat menjalankan modul tersebut untuk memastikan bahwa perubahan Anda tidak merusak kode. Tes-tes ini akan diabaikan ketika kode diimpor sebagai modul.
Modul ini akan berisi dua fungsi sederhana, dan jika Anda ingin tahu berapa kali fungsi-fungsi tersebut dipanggil, Anda perlu menginisialisasi penghitung ke nol ketika modul diimpor.
Anda dapat melakukannya dengan cara ini:
Memperkenalkan variabel seperti itu benar-benar benar, tetapi dapat menyebabkan efek samping penting yang harus Anda ketahui.
Lihatlah file main.py yang dimodifikasi:
Seperti yang bisa Anda lihat, file utama mencoba mengakses variabel penghitung modul. Apakah ini legal? Ya, itu legal. Apakah itu dapat digunakan? Mungkin sangat bermanfaat. Apakah itu aman?
Itu tergantung - jika Anda mempercayai pengguna modul Anda, tidak ada masalah; namun, Anda mungkin tidak ingin seluruh dunia melihat variabel pribadi Anda.
Berbeda dengan banyak bahasa pemrograman lainnya, Python tidak memiliki cara untuk menyembunyikan variabel tersebut dari mata pengguna modul.
Anda hanya dapat memberitahu pengguna Anda bahwa ini adalah variabel Anda, bahwa mereka dapat membacanya, tetapi bahwa mereka tidak boleh memodifikasinya dalam keadaan apa pun.
Ini dilakukan dengan menambahkan _ (satu garis bawah) atau __ (dua garis bawah) di depan nama variabel, tetapi ingat, itu hanya konvensi. Pengguna modul Anda dapat mengikuti atau tidak.
Tentu saja, kita akan mengikuti konvensi. Sekarang mari kita tambahkan dua fungsi ke dalam modul - mereka akan mengevaluasi jumlah dan produk dari angka-angka yang dikumpulkan dalam sebuah daftar.
Selain itu, mari kita tambahkan beberapa ornamen di sana dan hapus sisa-sisa yang tidak perlu.
Baiklah. Mari kita tulis beberapa kode yang baru dalam file module.py kita. Modul yang diperbarui sudah siap di sini:
Beberapa elemen perlu dijelaskan, kami pikir:
baris yang dimulai dengan #! memiliki banyak nama - dapat disebut shabang, shebang, hashbang, poundbang, atau bahkan hashpling (jangan tanya kami mengapa). Nama itu sendiri tidak berarti di sini - peranannya lebih penting. Dari sudut pandang Python, itu hanya komentar karena dimulai dengan #. Untuk sistem operasi Unix dan Unix-like (termasuk MacOS), baris seperti itu memberi instruksi pada sistem operasi bagaimana menjalankan isi file (dengan kata lain, program apa yang perlu diluncurkan untuk menafsirkan teks). Di beberapa lingkungan (terutama yang terkait dengan server web), ketidakhadiran baris tersebut akan menyebabkan masalah;
string (mungkin multiline) yang ditempatkan sebelum instruksi modul apa pun (termasuk impor) disebut doc-string, dan seharusnya menjelaskan dengan singkat tujuan dan isi modul;
fungsi yang didefinisikan di dalam modul (suml() dan prodl()) tersedia untuk diimpor;
kami telah menggunakan variabel __name__ untuk mendeteksi ketika file dijalankan secara mandiri, dan memanfaatkan kesempatan ini untuk melakukan beberapa tes sederhana.
Sekarang memungkinkan untuk menggunakan modul yang diperbarui - ini adalah salah satu cara:
Saatnya membuat contoh kami lebih rumit - sejauh ini kami mengasumsikan bahwa file Python utama terletak di folder/direktori yang sama dengan modul yang akan diimpor.
Mari kita lepaskan asumsi ini dan lakukan eksperimen berpikir berikut:
kami menggunakan sistem operasi Windows® (asumsi ini penting, karena bentuk nama file bergantung padanya)
skrip Python utama terletak di C:\Users\user\py\progs dan diberi nama main.py
modul yang akan diimpor terletak di C:\Users\user\py\modules
Bagaimana cara menanganinya?
Untuk menjawab pertanyaan ini, kita harus membahas bagaimana Python mencari modul. Ada variabel khusus (sebenarnya sebuah daftar) yang menyimpan semua lokasi (folder/direktori) yang dicari untuk menemukan modul yang diminta oleh instruksi impor.
Python menjelajahi folder-folder ini sesuai dengan urutan yang tercantum dalam daftar - jika modul tidak dapat ditemukan di salah satu dari direktori ini, impor akan gagal.
Jika tidak, folder pertama yang berisi modul dengan nama yang diinginkan akan dipertimbangkan (jika ada folder lain yang berisi modul dengan nama yang sama, itu akan diabaikan).
Variabel tersebut dinamai path, dan dapat diakses melalui modul bernama sys. Inilah cara Anda dapat memeriksa nilai reguler dari variabel tersebut:
Kami telah meluncurkan kode di dalam folder C:\User\user, dan inilah yang kami dapatkan:
Catatan: folder tempat eksekusi dimulai tercantum di elemen jalur pertama.
Catatan lagi: ada file zip yang tercantum sebagai salah satu elemen jalur - ini bukan kesalahan. Python dapat memperlakukan file zip sebagai folder biasa - ini dapat menghemat banyak penyimpanan.
Apakah Anda dapat mencari tahu bagaimana kita dapat menyelesaikan masalah kita sekarang? Kita bisa menambahkan folder yang berisi modul ke variabel jalur (ini sepenuhnya dapat dimodifikasi).
Salah satu dari beberapa solusi yang mungkin terlihat seperti ini:
Catatan:
kami telah menggandakan \ di dalam nama folder - apakah kamu tahu mengapa?
Karena garis miring terbalik () digunakan untuk melarikan karakter lain - jika Anda ingin mendapatkan hanya garis miring terbalik, Anda harus melarikannya.
kami telah menggunakan nama relatif dari folder - ini akan berfungsi jika Anda memulai file main.py langsung dari folder utamanya, dan tidak akan berfungsi jika direktori saat ini tidak sesuai dengan jalur relatif; Anda selalu dapat menggunakan jalur absolut, seperti ini:
Kami telah menggunakan metode append() - pada dasarnya, jalur baru akan menempati elemen terakhir dalam daftar jalur; jika Anda tidak menyukai ide tersebut, Anda dapat menggunakan metode insert() sebagai gantinya.
Bayangkan pada masa depan yang tidak terlalu jauh, Anda dan rekan-rekan Anda menulis sejumlah besar fungsi Python.
Tim Anda memutuskan untuk mengelompokkan fungsi-fungsi tersebut dalam modul-modul terpisah, dan ini adalah hasil akhir dari pengelompokan:
Catatan: kami telah menampilkan seluruh konten untuk modul alpha.py saja - anggaplah bahwa semua modul terlihat serupa (mereka berisi satu fungsi bernama funX, di mana X adalah huruf pertama dari nama modul).
Tiba-tiba, seseorang menyadari bahwa modul-modul ini membentuk hierarki mereka sendiri, sehingga meletakkannya dalam struktur datar tidak akan menjadi ide yang baik.
Setelah beberapa diskusi, tim mencapai kesimpulan bahwa modul harus dikelompokkan. Semua peserta setuju bahwa struktur pohon berikut ini sempurna mencerminkan hubungan timbal balik antara modul-modul tersebut:
Mari kita tinjau ini dari bawah ke atas:
kelompok jelek berisi dua modul: psi dan omega;
kelompok terbaik berisi dua modul: sigma dan tau;
kelompok baik berisi dua modul (alpha dan beta) dan satu subkelompok (terbaik)
kelompok ekstra berisi dua subkelompok (baik dan buruk) dan satu modul (iota)
Terlihat buruk? Tidak sama sekali - analisis strukturnya dengan hati-hati. Ini menyerupai sesuatu, bukan?
Ini terlihat seperti struktur direktori.
Mari kita bangun pohon yang mencerminkan dependensi yang diproyeksikan antara modul-modulnya.
Ini adalah tampilan saat ini dari struktur pohon (tree):
Struktur seperti ini hampir sama dengan paket (dalam artian Python). Namun, masih kurang rinci untuk menjadi fungsional dan operasional. Kami akan menyelesaikannya dalam sekejap.
Jika Anda mengasumsikan bahwa extra adalah nama dari paket yang baru dibuat (anggap saja sebagai root dari paket), maka itu akan menetapkan aturan penamaan yang memungkinkan Anda untuk dengan jelas menamai setiap entitas dari strukturnya.
Sebagai contoh:
lokasi fungsi bernama funT() dari paket tau dapat digambarkan sebagai:
sebuah fungsi yang ditandai sebagai:
Berasal dari modul psi yang disimpan di dalam subpaket jelek dari paket ekstra.
Ada dua pertanyaan yang harus dijawab:
Bagaimana kamu mentransformasikan pohon seperti itu (sebenarnya, subpohon) menjadi paket Python yang sesungguhnya (dengan kata lain, bagaimana kamu meyakinkan Python bahwa pohon seperti itu bukan hanya kumpulan file sampah, tetapi sekumpulan modul)?
Di mana kamu menempatkan subpohon tersebut agar dapat diakses oleh Python?
Jawaban untuk pertanyaan pertama cukup mengejutkan: paket, seperti modul, mungkin memerlukan inisialisasi.
Inisialisasi modul dilakukan dengan kode yang tidak terikat (bukan bagian dari fungsi apa pun) yang terletak di dalam file modul. Karena paket bukanlah sebuah file, teknik ini tidak berguna untuk menginisialisasi paket.
Sebaliknya, kamu perlu menggunakan trik yang berbeda - Python mengharapkan adanya file dengan nama yang sangat unik di dalam folder paket: init.py.
Konten file ini dieksekusi ketika salah satu modul dari paket diimpor. Jika kamu tidak ingin melakukan inisialisasi khusus, kamu dapat meninggalkan file ini kosong, tetapi jangan lupakan untuk membuatnya.
Ingat: keberadaan file init.py akhirnya membentuk paket.
Catatan: bukan hanya folder root yang dapat berisi file __init.py__ - Anda dapat menempatkannya di dalam subfolder (subpaketi) apa pun juga. Ini mungkin berguna jika beberapa subpaketi memerlukan perlakuan individual dan jenis inisialisasi khusus.
Sekarang saatnya menjawab pertanyaan kedua - jawabannya sederhana: di mana saja. Anda hanya perlu memastikan bahwa Python mengetahui lokasi paket. Anda sudah tahu cara melakukannya.
Anda siap untuk menggunakan paket pertama Anda.
Kita anggap bahwa lingkungan kerjanya terlihat seperti ini:
Kami telah menyiapkan sebuah file zip yang berisi semua file dari cabang paket. Kamu dapat mengunduhnya dan menggunakannya untuk eksperimenmu sendiri, tetapi ingat untuk mengekstraknya di folder yang ditampilkan pada skema, jika tidak, file tersebut tidak dapat diakses oleh kode dari file utama.
DOWNLOAD Modules and Packages ZIP file
Anda akan melanjutkan eksperimen Anda menggunakan file main2.py.
Kami akan mengakses fungsi funI() dari modul iota dari bagian atas paket tambahan. Ini memaksa kita untuk menggunakan nama paket yang berkualifikasi (kaitkan ini dengan memberi nama folder dan subfolder - konvensinya sangat mirip).
Berikut cara melakukannya:
Catatan:
kami telah memodifikasi variabel path agar dapat diakses oleh Python;
import tidak menunjuk langsung ke modul, namun menentukan jalur lengkap dari puncak paket;
mengganti import extra.iota dengan import iota akan menyebabkan kesalahan.
Varian berikut juga valid:
Perhatikan nama kualifikasi dari modul iota.
Sekarang mari kita mencapai bagian paling bawah pohon - ini adalah cara untuk mengakses modul sigma dan tau:
Anda dapat membuat hidup Anda lebih mudah dengan menggunakan aliasing:
Mari kita anggap bahwa kami telah mengompresi seluruh subdirektori, dimulai dari folder tambahan (termasuk itu), dan mari kita dapatkan file yang bernama extrapack.zip. Selanjutnya, kita meletakkan file tersebut di dalam folder paket.
Sekarang kita dapat menggunakan file zip sebagai paket:
Jika Anda ingin melakukan eksperimen sendiri dengan paket yang telah kami buat, Anda dapat mengunduhnya di bawah ini. Kami mendorong Anda untuk melakukannya.
DOWNLOAD Extrapack ZIP file
Sekarang Anda dapat membuat modul dan menggabungkannya menjadi paket. Sudah saatnya memulai diskusi yang benar-benar berbeda - tentang kesalahan, kegagalan, dan kerusakan.
Sementara modul dirancang untuk menghubungkan beberapa entitas terkait (fungsi, variabel, konstanta, dll.), paket adalah kontainer yang memungkinkan penggabungan beberapa modul terkait di bawah satu nama umum. Kontainer seperti itu dapat didistribusikan apa adanya (sebagai kumpulan file yang diterapkan dalam sub-pohon direktori) atau dapat dikemas dalam file zip.
Selama impor pertama dari modul aktual, Python menerjemahkan kode sumbernya ke dalam format semi-dikompilasi yang disimpan di dalam file pyc, dan menyebarluaskan file-file tersebut ke dalam direktori __pycache__ yang terletak di direktori asal modul.
Jika Anda ingin memberi tahu pengguna modul Anda bahwa entitas tertentu harus dianggap sebagai privat (yaitu tidak boleh digunakan secara eksplisit di luar modul), Anda dapat menandai namanya dengan awalan _ atau __. Jangan lupa bahwa ini hanya rekomendasi, bukan perintah.
Nama shabang, shebang, hasbang, poundbang, dan hashpling menggambarkan digraf yang ditulis sebagai #!, yang digunakan untuk memberi instruksi pada sistem operasi mirip Unix tentang cara meluncurkan file sumber Python. Konvensi ini tidak berpengaruh di bawah sistem operasi MS Windows.
Jika Anda ingin meyakinkan Python agar memperhatikan direktori paket non-standar, namanya perlu dimasukkan/ditambahkan ke dalam daftar direktori impor yang disimpan dalam variabel path yang terdapat dalam modul sys.
Sebuah file Python bernama __init__.py secara implisit dijalankan ketika suatu paket yang mengandungnya diimpor, dan digunakan untuk menginisialisasi paket dan/atau sub-paketnya (jika ada). File ini mungkin kosong, tetapi tidak boleh tidak ada.
Latihan 1
Anda ingin mencegah pengguna modul Anda menjalankan kode Anda sebagai skrip biasa. Bagaimana Anda akan mencapai efek tersebut?
Latihan 2
Beberapa paket tambahan dan penting disimpan di dalam direktori D:\Python\Project\Modules. Tuliskan kode yang memastikan bahwa direktori tersebut dapat diakses oleh Python untuk menemukan semua modul yang diminta.
Latihan 3
Direktori yang disebutkan pada latihan sebelumnya berisi sub-pohon dengan struktur berikut: