PEP 08 menyatakan:
Impor selalu diletakkan di bagian atas file, tepat setelah komentar modul dan dokumen, dan sebelum modul global dan konstanta.
Namun jika kelas / metode / fungsi yang saya impor hanya digunakan dalam kasus yang jarang terjadi, tentunya lebih efisien untuk melakukan impor saat dibutuhkan?
Bukankah ini:
class SomeClass(object):
def not_often_called(self)
from datetime import datetime
self.datetime = datetime.now()
lebih efisien dari ini?
from datetime import datetime
class SomeClass(object):
def not_often_called(self)
self.datetime = datetime.now()
python
optimization
coding-style
Adam J. Forster
sumber
sumber
Menempatkan pernyataan impor di dalam suatu fungsi dapat mencegah dependensi melingkar. Misalnya, jika Anda memiliki 2 modul, X.py dan Y.py, dan keduanya harus saling mengimpor, ini akan menyebabkan ketergantungan melingkar ketika Anda mengimpor salah satu modul yang menyebabkan loop tak terbatas. Jika Anda memindahkan pernyataan impor di salah satu modul, maka ia tidak akan mencoba mengimpor modul lain hingga fungsinya dipanggil, dan modul itu sudah akan diimpor, jadi tidak ada loop tanpa batas. Baca di sini untuk lebih lanjut - effbot.org/zone/import-confusion.htm
sumber
Saya telah mengadopsi praktik meletakkan semua impor dalam fungsi yang menggunakannya, bukan di bagian atas modul.
Manfaat yang saya dapatkan adalah kemampuan untuk refactor lebih andal. Ketika saya memindahkan fungsi dari satu modul ke modul lainnya, saya tahu bahwa fungsi tersebut akan terus bekerja dengan semua warisan pengujian yang masih ada. Jika saya memiliki impor saya di bagian atas modul, ketika saya memindahkan fungsi, saya menemukan bahwa saya menghabiskan banyak waktu untuk mendapatkan impor modul baru yang lengkap dan minimal. IDE refactoring mungkin membuat ini tidak relevan.
Ada penalti kecepatan seperti yang disebutkan di tempat lain. Saya telah mengukur ini dalam aplikasi saya dan ternyata tidak signifikan untuk tujuan saya.
Ini juga bagus untuk dapat melihat semua dependensi modul di depan tanpa menggunakan pencarian (misalnya grep). Namun, alasan saya peduli dengan dependensi modul umumnya karena saya menginstal, refactoring, atau memindahkan seluruh sistem yang terdiri dari beberapa file, bukan hanya satu modul. Dalam hal ini, saya akan tetap melakukan pencarian global untuk memastikan saya memiliki dependensi tingkat sistem. Jadi saya belum menemukan impor global untuk membantu pemahaman saya tentang suatu sistem dalam praktiknya.
Saya biasanya memasukkan impor
sys
di dalamif __name__=='__main__'
cek dan kemudian meneruskan argumen (sepertisys.argv[1:]
) ke suatumain()
fungsi. Ini memungkinkan saya untuk menggunakanmain
dalam konteks di manasys
belum diimpor.sumber
def main(): print(sys.argv); if True: import sys; main();
Anda harus membungkusif __name__=='__main__'
fungsi untuk membuat namespace baru.Sebagian besar waktu ini akan berguna untuk kejelasan dan masuk akal untuk dilakukan tetapi tidak selalu demikian. Berikut adalah beberapa contoh keadaan di mana impor modul mungkin tinggal di tempat lain.
Pertama, Anda bisa memiliki modul dengan unit test formulir:
Kedua, Anda mungkin memiliki persyaratan untuk mengimpor beberapa modul yang berbeda saat runtime.
Mungkin ada situasi lain di mana Anda dapat menempatkan impor di bagian lain dalam kode.
sumber
Varian pertama memang lebih efisien daripada yang kedua ketika fungsinya disebut nol atau satu kali. Dengan doa kedua dan selanjutnya, pendekatan "impor setiap panggilan" sebenarnya kurang efisien. Lihat tautan ini untuk teknik pemuatan malas yang menggabungkan yang terbaik dari kedua pendekatan dengan melakukan "impor malas".
Tetapi ada alasan lain selain efisiensi mengapa Anda lebih suka yang satu daripada yang lain. Salah satu pendekatan adalah membuatnya lebih jelas bagi seseorang yang membaca kode tentang dependensi yang dimiliki modul ini. Mereka juga memiliki karakteristik kegagalan yang sangat berbeda - yang pertama akan gagal pada waktu pengambilan jika tidak ada modul "datetime" sementara yang kedua tidak akan gagal sampai metode dipanggil.
Catatan yang ditambahkan: Di IronPython, impor bisa sedikit lebih mahal daripada di CPython karena kode pada dasarnya sedang dikompilasi karena sedang diimpor.
sumber
Curt membuat poin yang bagus: versi kedua lebih jelas dan akan gagal pada waktu buka daripada nanti, dan secara tak terduga.
Biasanya saya tidak khawatir tentang efisiensi memuat modul, karena itu (a) cukup cepat, dan (b) kebanyakan hanya terjadi saat startup.
Jika Anda harus memuat modul kelas berat pada waktu yang tidak terduga, mungkin lebih masuk akal untuk memuatnya secara dinamis dengan
__import__
fungsi tersebut, dan pastikan untuk menangkapImportError
pengecualian, dan menanganinya dengan cara yang masuk akal.sumber
Saya tidak akan khawatir tentang efisiensi memuat modul di depan terlalu banyak. Memori yang diambil oleh modul tidak akan terlalu besar (dengan asumsi itu cukup modular) dan biaya awal akan diabaikan.
Dalam kebanyakan kasus, Anda ingin memuat modul di bagian atas file sumber. Untuk seseorang yang membaca kode Anda, akan lebih mudah untuk mengetahui fungsi atau objek apa yang berasal dari modul apa.
Salah satu alasan bagus untuk mengimpor modul di tempat lain dalam kode adalah jika itu digunakan dalam pernyataan debugging.
Sebagai contoh:
Saya bisa men-debug ini dengan:
Tentu saja, alasan lain untuk mengimpor modul di tempat lain dalam kode adalah jika Anda perlu mengimpornya secara dinamis. Ini karena Anda hampir tidak punya pilihan.
Saya tidak akan khawatir tentang efisiensi memuat modul di depan terlalu banyak. Memori yang diambil oleh modul tidak akan terlalu besar (dengan asumsi itu cukup modular) dan biaya awal akan diabaikan.
sumber
Ini merupakan tradeoff, yang hanya dapat diputuskan oleh programmer.
Kasus 1 menghemat sebagian memori dan waktu startup dengan tidak mengimpor modul datetime (dan melakukan inisialisasi apa pun yang diperlukan) hingga diperlukan. Perhatikan bahwa melakukan impor 'hanya ketika dipanggil' juga berarti melakukannya 'setiap kali ketika dipanggil', sehingga setiap panggilan setelah yang pertama masih menimbulkan biaya tambahan tambahan untuk melakukan impor.
Kasus 2 menghemat waktu pelaksanaan dan latensi dengan mengimpor datetime sebelumnya sehingga not_often_called () akan kembali lebih cepat ketika sedang disebut, dan juga dengan tidak menimbulkan biaya overhead impor pada setiap panggilan.
Selain efisiensi, lebih mudah untuk melihat dependensi modul di depan jika pernyataan impor ... di depan. Menyembunyikannya dalam kode dapat membuatnya lebih sulit untuk dengan mudah menemukan modul apa yang menjadi sandarannya.
Secara pribadi saya biasanya mengikuti PEP kecuali untuk hal-hal seperti tes unit dan sehingga saya tidak ingin selalu dimuat karena saya tahu mereka tidak akan digunakan kecuali untuk kode tes.
sumber
sys.modules
dengan mudah dapat diimbangi dengan penghematan hanya dengan harus mencari nama lokal, bukan nama global.Berikut adalah contoh di mana semua impor berada di bagian paling atas (ini adalah satu-satunya waktu saya perlu melakukan ini) Saya ingin dapat menghentikan subproses pada Un * x dan Windows.
(Pada ulasan: apa yang dikatakan John Millikin .)
sumber
Ini seperti banyak optimasi lainnya - Anda mengorbankan keterbacaan untuk kecepatan. Seperti yang disebutkan oleh John, jika Anda telah melakukan pekerjaan rumah profil Anda dan menemukan ini sebagai perubahan yang cukup bermanfaat dan Anda membutuhkan kecepatan ekstra, maka lakukanlah. Mungkin baik untuk mencatat dengan semua impor lainnya:
sumber
Inisialisasi modul hanya terjadi sekali - pada impor pertama. Jika modul yang dimaksud adalah dari pustaka standar, maka Anda kemungkinan akan mengimpornya dari modul lain di program Anda juga. Untuk sebuah modul yang lazim seperti masa-masa, itu juga kemungkinan merupakan ketergantungan bagi banyak perpustakaan standar lainnya. Pernyataan impor akan menelan biaya sangat sedikit karena sejak modul akan terjadi sudah terjadi. Semua yang dilakukannya saat ini adalah mengikat objek modul yang ada ke lingkup lokal.
Pasangan informasi itu dengan argumen untuk keterbacaan dan saya akan mengatakan bahwa yang terbaik adalah memiliki pernyataan impor pada ruang lingkup modul.
sumber
Hanya untuk melengkapi jawaban Moe dan pertanyaan aslinya:
Ketika kita harus berurusan dengan ketergantungan sirkuler kita dapat melakukan beberapa "trik". Dengan asumsi kami bekerja dengan modul
a.py
danb.py
yang berisix()
dan by()
, masing-masing. Kemudian:from imports
di bagian bawah modul.from imports
dalam fungsi atau metode yang sebenarnya membutuhkan impor (ini tidak selalu mungkin, karena Anda dapat menggunakannya dari beberapa tempat).from imports
menjadi impor yang terlihat seperti:import a
Jadi, untuk menyimpulkan. Jika Anda tidak berurusan dengan dependensi melingkar dan melakukan semacam trik untuk menghindarinya, maka lebih baik untuk meletakkan semua impor Anda di atas karena alasan yang sudah dijelaskan dalam jawaban lain untuk pertanyaan ini. Dan tolong, ketika melakukan "trik" ini termasuk komentar, itu selalu diterima! :)
sumber
Selain jawaban luar biasa yang telah diberikan, perlu dicatat bahwa penempatan impor bukan hanya masalah gaya. Kadang-kadang modul memiliki dependensi implisit yang perlu diimpor atau diinisialisasi terlebih dahulu, dan impor tingkat atas dapat menyebabkan pelanggaran terhadap urutan eksekusi yang diperlukan.
Masalah ini sering muncul di API Python Apache Spark, di mana Anda perlu menginisialisasi SparkContext sebelum mengimpor paket atau modul pyspark. Yang terbaik adalah menempatkan impor pyspark dalam cakupan di mana SparkContext dijamin akan tersedia.
sumber
Saya terkejut tidak melihat angka biaya aktual untuk pemeriksaan beban berulang yang sudah diposting, meskipun ada banyak penjelasan bagus tentang apa yang diharapkan.
Jika Anda mengimpor di bagian atas, Anda menerima pukulan apa pun yang terjadi. Itu cukup kecil, tetapi biasanya dalam milidetik, bukan nanodetik.
Jika Anda mengimpor dalam suatu fungsi, maka Anda hanya menerima klik untuk memuat jika dan ketika salah satu dari fungsi tersebut pertama kali dipanggil. Seperti yang telah ditunjukkan banyak orang, jika itu tidak terjadi sama sekali, Anda menghemat waktu muat. Tetapi jika fungsi dipanggil banyak, Anda mengambil hit berulang meskipun jauh lebih kecil (untuk memeriksa bahwa itu telah dimuat; bukan untuk benar-benar memuat ulang). Di sisi lain, seperti yang ditunjukkan @aaronasterling, Anda juga sedikit menghemat karena mengimpor dalam suatu fungsi memungkinkan fungsi menggunakan pencarian variabel lokal yang sedikit lebih cepat untuk mengidentifikasi nama nanti ( http://stackoverflow.com/questions/477096/python- import-coding-style / 4789963 # 4789963 )
Berikut ini adalah hasil dari tes sederhana yang mengimpor beberapa hal dari dalam suatu fungsi. Waktu yang dilaporkan (dalam Python 2.7.14 pada 2.3 GHz Intel Core i7) ditunjukkan di bawah ini (panggilan ke-2 yang mengambil lebih banyak dari panggilan-panggilan selanjutnya tampaknya konsisten, meskipun saya tidak tahu mengapa).
Kode:
sumber
Saya tidak bercita-cita untuk memberikan jawaban yang lengkap, karena orang lain telah melakukan ini dengan sangat baik. Saya hanya ingin menyebutkan satu use case ketika saya menemukan sangat berguna untuk mengimpor modul di dalam fungsi. Aplikasi saya menggunakan paket python dan modul yang disimpan di lokasi tertentu sebagai plugin. Selama startup aplikasi, aplikasi berjalan melalui semua modul di lokasi dan mengimpornya, kemudian melihat ke dalam modul dan jika menemukan beberapa titik pemasangan untuk plugin (dalam kasus saya itu adalah subkelas dari kelas dasar tertentu yang memiliki unik ID) itu mendaftarkan mereka. Jumlah plugin besar (sekarang puluhan, tapi mungkin ratusan di masa depan) dan masing-masing jarang digunakan. Memiliki impor perpustakaan pihak ketiga di bagian atas modul plugin saya adalah penalti sedikit selama startup aplikasi. Terutama beberapa perpustakaan pihak ketiga yang berat untuk diimpor (mis. Impor plotly bahkan mencoba untuk terhubung ke internet dan mengunduh sesuatu yang menambahkan sekitar satu detik untuk startup). Dengan mengoptimalkan impor (memanggil mereka hanya dalam fungsi di mana mereka digunakan) di plugin saya berhasil mengecilkan startup dari 10 detik menjadi sekitar 2 detik. Itu adalah perbedaan besar bagi pengguna saya.
Jadi jawaban saya adalah tidak, jangan selalu meletakkan impor di bagian atas modul Anda.
sumber
Sangat menarik bahwa tidak ada satu jawaban yang menyebutkan pemrosesan paralel sejauh ini, di mana mungkin DIBUTUHKAN bahwa impor ada dalam fungsi, ketika kode fungsi serial adalah apa yang didorong ke inti lainnya, misalnya seperti dalam kasus ipyparallel.
sumber
Mungkin ada peningkatan kinerja dengan mengimpor variabel / pelingkupan lokal di dalam suatu fungsi. Ini tergantung pada penggunaan barang yang diimpor di dalam fungsi. Jika Anda mengulang berulang kali dan mengakses objek global modul, mengimpornya sebagai lokal dapat membantu.
test.py
runlocal.py
run.py
Waktu di Linux menunjukkan keuntungan kecil
sebenarnya adalah jam dinding. pengguna adalah waktu dalam program. sys adalah waktu untuk panggilan sistem.
https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names
sumber
Keterbacaan
Selain kinerja startup, ada argumen keterbacaan yang dibuat untuk melokalkan
import
pernyataan. Misalnya, ambil nomor baris python 1283 hingga 1296 dalam proyek python pertama saya saat ini:Jika
import
pernyataan itu ada di bagian atas file saya harus gulir ke atas, atau tekan Home, untuk mencari tahu apaET
itu. Maka saya harus menavigasi kembali ke baris 1283 untuk melanjutkan membaca kode.Memang, bahkan jika
import
pernyataan itu di atas fungsi (atau kelas) karena banyak yang akan menempatkannya, paging naik dan turun akan diperlukan.Menampilkan nomor versi Gnome jarang akan dilakukan sehingga bagian
import
atas file memperkenalkan jeda startup yang tidak perlu.sumber
Saya ingin menyebutkan usecase milik saya, sangat mirip dengan yang disebutkan oleh @John Millikin dan @VK:
Impor Opsional
Saya melakukan analisis data dengan Jupyter Notebook, dan saya menggunakan notebook IPython yang sama sebagai templat untuk semua analisis. Dalam beberapa kesempatan, saya perlu mengimpor Tensorflow untuk melakukan beberapa model cepat, tetapi kadang-kadang saya bekerja di tempat-tempat di mana tensorflow tidak diatur / lambat untuk diimpor. Dalam kasus tersebut, saya merangkum operasi Tensorflow saya yang bergantung pada fungsi pembantu, mengimpor tensorflow di dalam fungsi itu, dan mengikatnya ke tombol.
Dengan cara ini, saya bisa melakukan "restart-and-run-all" tanpa harus menunggu impor, atau harus melanjutkan sisa sel ketika gagal.
sumber
Ini adalah diskusi yang menarik. Seperti banyak orang lain, saya bahkan tidak pernah mempertimbangkan topik ini. Saya terpojok karena harus mengimpor fungsi karena ingin menggunakan ORANG Django di salah satu perpustakaan saya. Saya harus menelepon
django.setup()
sebelum mengimpor kelas model saya dan karena ini di bagian atas file itu sedang diseret ke dalam kode perpustakaan sepenuhnya non-Django karena konstruksi injektor IoC.Saya agak meretas sekitar dan akhirnya menempatkan
django.setup()
di konstruktor singleton dan impor yang relevan di bagian atas setiap metode kelas. Sekarang ini berfungsi dengan baik tetapi membuat saya gelisah karena impor tidak di atas dan saya juga mulai khawatir tentang tambahan waktu impor. Lalu saya datang ke sini dan membaca dengan penuh minat semua orang mengambil ini.Saya memiliki latar belakang C ++ yang panjang dan sekarang menggunakan Python / Cython. Menurut saya ini adalah mengapa tidak menempatkan impor dalam fungsi kecuali itu menyebabkan Anda mengalami kemacetan yang diprofilkan. Ini hanya seperti mendeklarasikan ruang untuk variabel sebelum Anda membutuhkannya. Masalahnya adalah saya memiliki ribuan baris kode dengan semua impor di atas! Jadi saya pikir saya akan melakukannya mulai sekarang dan mengubah file aneh di sana-sini ketika saya melewati dan punya waktu.
sumber