Kami hadir untuk perhatian Anda bagian kedua dari terjemahan materi yang dikhususkan untuk fitur bekerja dengan modul dalam proyek-proyek Instagram Python. Bagian
pertama dari terjemahan memberi gambaran umum tentang situasi dan menunjukkan dua masalah. Salah satunya menyangkut lambatnya permulaan server, yang kedua - efek samping dari perintah impor yang tidak aman. Hari ini pembicaraan ini akan berlanjut. Kami akan mempertimbangkan masalah lain dan berbicara tentang pendekatan untuk menyelesaikan semua masalah yang diangkat.

Masalah 3: status global yang bisa berubah
Lihatlah kategori kesalahan umum lainnya.
def myview(request): SomeClass.id = request.GET.get("id")
Di sini kita berada dalam fungsi presentasi dan melampirkan atribut ke kelas tertentu berdasarkan data yang diterima dari permintaan. Anda mungkin sudah memahami inti permasalahannya. Faktanya adalah bahwa kelas adalah singletones global. Dan di sini kita menempatkan negara, tergantung pada permintaan, di objek berumur panjang. Dalam proses server web yang membutuhkan waktu lama untuk diselesaikan, ini dapat menyebabkan pencemaran setiap permintaan di masa mendatang yang dibuat sebagai bagian dari proses ini.
Hal yang sama dapat dengan mudah terjadi dalam tes. Khususnya, dalam kasus di mana programmer mencoba menggunakan
patch monyet dan tidak menggunakan
manajer konteks , seperti
mock.patch
. Hal ini dapat menyebabkan tidaknya pencemaran permintaan, tetapi pencemaran semua tes yang akan dilakukan dalam proses yang sama. Ini adalah alasan serius untuk perilaku yang tidak dapat diandalkan dari sistem pengujian kami. Ini adalah masalah yang signifikan, dan sangat sulit untuk mencegahnya. Akibatnya, kami meninggalkan sistem pengujian terpadu dan beralih ke skema isolasi pengujian, yang dapat digambarkan sebagai "satu pengujian per proses".
Sebenarnya, ini adalah masalah ketiga kami. Negara global yang bisa berubah adalah fenomena yang tidak unik untuk Python. Anda dapat menemukannya di mana saja. Kita berbicara tentang kelas, modul, daftar atau kamus yang melekat pada modul atau kelas, tentang objek tunggal yang dibuat pada tingkat modul. Bekerja dalam lingkungan seperti itu membutuhkan disiplin. Untuk mencegah polusi negara global saat program sedang berjalan, Anda memerlukan pengetahuan yang sangat baik tentang Python.
Memperkenalkan modul ketat
Salah satu akar penyebab masalah kami adalah kami menggunakan Python untuk menyelesaikan masalah yang tidak dirancang untuk bahasa ini. Dalam tim kecil dan proyek kecil, jika Anda mengikuti aturan saat menggunakan Python, bahasa ini berfungsi dengan baik. Dan kita harus pergi ke bahasa yang lebih keras.
Tetapi basis kode kami telah melebihi ukuran yang memungkinkan kami untuk setidaknya berbicara tentang cara menulis ulang dalam bahasa lain. Dan, yang lebih penting, terlepas dari semua masalah yang kita hadapi, Python memiliki banyak hal untuk dilakukan. Dia memberi kita lebih baik daripada buruk. Pengembang kami sangat menyukai bahasa ini. Akibatnya, itu hanya tergantung pada kita bagaimana mendapatkan Python untuk bekerja pada skala kita, dan bagaimana memastikan bahwa kita dapat terus bekerja pada proyek saat itu berkembang.
Menemukan solusi untuk masalah kami membawa kami ke satu ide. Ini terdiri dari penggunaan modul yang ketat.
Modul ketat adalah modul Python dari tipe baru, di mana ada konstruksi
__strict__ = True
. Mereka diimplementasikan menggunakan banyak mekanisme ekstensibilitas tingkat rendah yang sudah dimiliki Python. Sebuah
pemuat modul khusus mem-parsing kode menggunakan modul
ast
, melakukan interpretasi abstrak dari kode yang dimuat untuk menganalisisnya, menerapkan berbagai transformasi ke AST, dan kemudian mengkompilasi AST kembali ke bytecode Python menggunakan fungsi
compile
bawaan.
Tidak ada efek samping impor
Modul ketat memberlakukan beberapa batasan pada apa yang bisa terjadi di tingkat modul. Jadi, semua kode tingkat modul (termasuk dekorator dan fungsi / inisialisasi yang disebut di tingkat modul) harus bersih, yaitu, kode yang bebas dari efek samping dan tidak menggunakan mekanisme I / O. Kondisi ini diperiksa oleh penerjemah abstrak menggunakan sarana analisis kode statis pada waktu kompilasi.
Ini berarti bahwa menggunakan modul ketat tidak menimbulkan efek samping saat mengimpornya. Kode yang dieksekusi selama impor modul tidak lagi dapat menyebabkan masalah yang tidak terduga. Karena fakta bahwa kami memeriksa ini pada tingkat interpretasi abstrak, menggunakan alat yang memahami sebagian besar Python, kami menghilangkan kebutuhan untuk terlalu membatasi ekspresi Python. Banyak jenis kode dinamis, tanpa efek samping, dapat digunakan dengan aman di tingkat modul. Ini termasuk berbagai dekorator dan definisi konstanta level modul menggunakan daftar atau generator kamus.
Mari kita perjelas, pertimbangkan sebuah contoh. Berikut adalah modul ketat yang ditulis dengan benar:
"""Module docstring.""" __strict__ = True from utils import log_to_network MY_LIST = [1, 2, 3] MY_DICT = {x: x+1 for x in MY_LIST} def log_calls(func): def _wrapped(*args, **kwargs): log_to_network(f"{func.__name__} called!") return func(*args, **kwargs) return _wrapped @log_calls def hello_world(): log_to_network("Hello World!")
Dalam modul ini, kita bisa menggunakan konstruksi Python biasa, termasuk kode dinamis, yang digunakan untuk membuat kamus, dan yang menggambarkan dekorator tingkat modul. Pada saat yang sama, mengakses sumber daya jaringan dalam fungsi
_wrapped
atau
_wrapped
sepenuhnya normal. Faktanya adalah bahwa mereka tidak dipanggil pada level modul.
Tetapi jika kita memindahkan panggilan
log_to_network
ke fungsi
log_calls
eksternal, atau jika kita mencoba menggunakan dekorator yang menyebabkan efek samping (seperti
@route
dari contoh sebelumnya), atau jika kita menggunakan panggilan
hello_world()
pada tingkat modul, maka itu akan berhenti menjadi sangat ketat. -module.
Bagaimana mengetahui bahwa tidak aman untuk memanggil fungsi
log_to_network
atau
route
di tingkat modul? Kami melanjutkan dari asumsi bahwa segala sesuatu yang diimpor dari modul yang bukan modul ketat tidak aman, dengan pengecualian beberapa fungsi dari perpustakaan standar yang diketahui aman. Jika modul
utils
adalah modul yang ketat, maka kita bisa mengandalkan analisis modul kita untuk memberi tahu kami apakah fungsi
log_to_network
.
Selain meningkatkan keandalan kode, impor yang tidak memiliki efek samping menghilangkan penghalang serius untuk mengamankan unduhan kode tambahan. Ini membuka kemungkinan lain untuk mengeksplorasi cara mempercepat tim impor. Jika kode level modul bebas dari efek samping, ini berarti bahwa kita dapat dengan aman menjalankan instruksi modul individual dalam mode "malas", berdasarkan permintaan, ketika mengakses atribut modul. Ini jauh lebih baik daripada mengikuti algoritma "serakah", dalam aplikasi yang semua kode modul dieksekusi sebelumnya. Dan, dengan mempertimbangkan fakta bahwa bentuk semua kelas dalam modul ketat sepenuhnya diketahui pada waktu kompilasi, di masa depan kita bahkan dapat mencoba mengatur penyimpanan permanen metadata modul (kelas, fungsi, konstanta) yang dihasilkan oleh eksekusi kode. Ini akan memungkinkan kami untuk mengatur impor cepat modul yang tidak berubah, yang tidak memerlukan eksekusi bytecode tingkat modul yang berulang.
Atribut kekebalan dan __slots__
Modul dan kelas ketat yang dideklarasikan di dalamnya tidak dapat diubah setelah dibuat. Modul dibuat tidak berubah dengan bantuan transformasi internal dari tubuh modul menjadi fungsi di mana akses ke semua variabel global diatur melalui variabel penutupan. Perubahan-perubahan ini telah secara serius mengurangi kemungkinan perubahan acak di negara global, meskipun keadaan global yang dapat berubah masih dapat diselesaikan jika diputuskan untuk menggunakannya melalui modul tingkat kontainer yang bisa berubah.
Anggota kelas yang dideklarasikan dalam modul ketat juga harus dinyatakan dalam
__init__
. Mereka secara otomatis ditulis ke atribut
__slots__
selama transformasi AST yang dilakukan oleh pemuat modul. Akibatnya, nanti Anda tidak bisa lagi melampirkan atribut tambahan ke instance kelas. Ini kelas yang serupa:
class Person: def __init__(self, name, age): self.name = name self.age = age
Selama transformasi AST, yang dilakukan selama pemrosesan modul ketat, operasi penetapan nilai ke
name
dan atribut
age
dilakukan dalam
__init__
akan dideteksi, dan atribut bentuk
__slots__ = ('name', 'age')
akan dilampirkan ke kelas. Ini akan mencegah atribut lainnya ditambahkan ke instance kelas. (Jika jenis anotasi digunakan, maka kami mempertimbangkan informasi akun tentang jenis yang tersedia di tingkat kelas, seperti
name: str
, dan juga menambahkannya ke daftar slot).
Batasan yang dijelaskan tidak hanya membuat kode lebih dapat diandalkan. Mereka membantu mempercepat eksekusi kode. Transformasi otomatis kelas dengan penambahan atribut
__slots__
meningkatkan efisiensi penggunaan memori ketika bekerja dengan kelas-kelas ini. Ini memungkinkan Anda untuk menyingkirkan pencarian kamus saat bekerja dengan instance kelas individual, yang mempercepat akses ke atribut. Selain itu, kami dapat terus mengoptimalkan pola-pola ini selama pelaksanaan kode Python, yang akan memungkinkan kami untuk lebih meningkatkan sistem kami.
Ringkasan
Modul yang ketat masih merupakan teknologi eksperimental. Kami memiliki prototipe yang berfungsi, kami masih dalam tahap awal mengembangkan kemampuan ini dalam produksi. Kami berharap setelah mendapatkan pengalaman yang cukup dalam menggunakan modul-ketat, kami akan dapat berbicara lebih banyak tentang mereka.
Pembaca yang budiman! Apakah Anda pikir fitur yang ditawarkan oleh modul ketat berguna dalam proyek Python Anda?
