Saya memiliki server Python yang sudah berjalan lama dan ingin dapat meningkatkan layanan tanpa me-restart server. Apa cara terbaik melakukan ini?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Saya memiliki server Python yang sudah berjalan lama dan ingin dapat meningkatkan layanan tanpa me-restart server. Apa cara terbaik melakukan ini?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Jawaban:
Anda dapat memuat ulang modul ketika sudah diimpor dengan menggunakan reload
fungsi builtin (hanya Python 3.4+) :
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
Dalam Python 3, reload
dipindahkan ke imp
modul. Dalam 3.4, imp
tidak digunakan lagi importlib
, dan reload
ditambahkan ke yang terakhir. Saat menargetkan 3 atau lebih baru, rujuk modul yang sesuai saat memanggil reload
atau mengimpornya.
Saya pikir inilah yang Anda inginkan. Server web seperti server pengembangan Django menggunakan ini sehingga Anda dapat melihat efek dari perubahan kode Anda tanpa memulai ulang proses server itu sendiri.
Mengutip dari dokumen:
Kode modul python dikompilasi ulang dan kode level modul dieksekusi kembali, mendefinisikan seperangkat objek baru yang terikat pada nama-nama dalam kamus modul. Fungsi init dari modul ekstensi tidak disebut kedua kalinya. Seperti semua objek lain dalam Python, objek lama hanya direklamasi setelah jumlah referensi turun menjadi nol. Nama-nama dalam namespace modul diperbarui untuk menunjuk ke objek baru atau yang diubah. Referensi lain ke objek lama (seperti nama di luar modul) tidak kembali untuk merujuk ke objek baru dan harus diperbarui di setiap ruang nama tempat mereka muncul jika diinginkan.
Seperti yang Anda catat dalam pertanyaan Anda, Anda harus merekonstruksi Foo
objek jika Foo
kelas berada di foo
modul.
X
bukan modul, Anda bisaimport sys; reload(sys.modules[X.__module__])
is_changed
fungsi ini hanyalah fungsi arbitrer yang harus Anda tulis; ini bukan built-in. Sebagai contoh, itu bisa membuka file yang sesuai dengan modul yang Anda impor dan memperbaikinya dengan versi cache untuk melihat apakah itu berubah.
Dalam Python 3.0–3.3 Anda akan menggunakan: imp.reload(module)
The BDFL telah menjawab pertanyaan ini.
Namun, imp
sudah usang dalam 3.4, mendukungimportlib
(terima kasih @Stefan! ).
Saya pikir , oleh karena itu, sekarang Anda akan menggunakan importlib.reload(module)
, meskipun saya tidak yakin.
reload(__builtins__)
berlaku di 2.x
Ini bisa sangat sulit untuk menghapus modul jika itu bukan Python murni.
Berikut ini beberapa informasi dari: Bagaimana cara saya benar-benar menghapus modul yang diimpor?
Anda dapat menggunakan sys.getrefcount () untuk mengetahui jumlah referensi aktual.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Angka yang lebih besar dari 3 menunjukkan bahwa akan sulit untuk menyingkirkan modul. Modul "kosong" (tidak mengandung apa-apa) buatan sendiri harus diambil setelahnya
>>> del sys.modules["empty"]
>>> del empty
sebagai referensi ketiga adalah artefak dari fungsi getrefcount ().
setattr(package, "empty", None)
reload()
hanya memuat ulang modul paling atas, dan apa pun di dalamnya tidak akan dimuat ulang kecuali Anda terlebih dahulu menghapusnya dari sys.modules.
reload(module)
, tetapi hanya jika itu sepenuhnya berdiri sendiri. Jika ada hal lain yang memiliki referensi ke modul (atau objek apa pun yang termasuk dalam modul), maka Anda akan mendapatkan kesalahan halus dan aneh yang disebabkan oleh kode lama yang berkeliaran lebih lama dari yang Anda harapkan, dan hal-hal seperti isinstance
tidak bekerja di berbagai versi berbeda dari modul. kode yang sama.
Jika Anda memiliki dependensi satu arah, Anda juga harus memuat ulang semua modul yang bergantung pada modul yang dimuat ulang untuk menyingkirkan semua referensi ke kode lama. Dan kemudian memuat kembali modul yang bergantung pada modul yang dimuat ulang, secara rekursif.
Jika Anda memiliki dependensi melingkar, yang sangat umum misalnya ketika Anda berurusan dengan memuat ulang paket, Anda harus membongkar semua modul dalam grup sekaligus. Anda tidak dapat melakukan ini dengan reload()
karena itu akan mengimpor kembali setiap modul sebelum dependensinya telah di-refresh, memungkinkan referensi lama masuk ke modul-modul baru.
Satu-satunya cara untuk melakukannya dalam hal ini adalah meretas sys.modules
, yang agak tidak didukung. Anda harus melalui dan menghapus setiap sys.modules
entri yang ingin Anda muat ulang pada impor berikutnya, dan juga menghapus entri yang nilainya None
berurusan dengan masalah implementasi terkait dengan caching impor relatif yang gagal. Ini tidak terlalu bagus tetapi selama Anda memiliki satu set dependensi mandiri yang tidak meninggalkan referensi di luar basis kode, itu bisa diterapkan.
Mungkin yang terbaik untuk me-restart server. :-)
None
nilai - nilai karena saya menjalankan persis masalah ini: Saya menghapus item dari sys.modules
dan setelah mengimpor kembali beberapa dependensi yang diimpor None
.
None
entri berhasil membuat jalan kembali melalui mekanisme impor ketika entri 'nyata' dihapus, dan saya tidak bisa mewujudkannya pada 2.7; di masa depan tentu itu tidak lagi menjadi masalah karena impor relatif implisit telah hilang. Sementara itu, menghapus semua entri dengan None
nilai tampaknya memperbaikinya.
reload
fungsinya? Ini built-in, Anda tidak perlu mengimpor perpustakaan apa pun.
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
, bahkan setelahreload(my_module)
%run my_module
[del(sys.modules[mod] for mod in sys.modules.keys() if mod.startswith('myModule.')]
.
import sys; import json; del sys.modules['json']; print(json.dumps([1]))
dan modul json masih berfungsi meskipun tidak ada di sys.modules lagi.
Untuk Python 2 gunakan fungsi reload bawaan () :
reload(module)
Untuk Python 2 dan 3.2-3.3 gunakan ulang dari modul imp :
import imp
imp.reload(module)
Tetapi imp
sudah ditinggalkan sejak versi 3.4 yang mendukung importlib , jadi gunakan:
import importlib
importlib.reload(module)
atau
from importlib import reload
reload(module)
from six import reload_module
(harus pip install six
terlebih dahulu tentu saja)
Kode berikut memungkinkan Anda kompatibilitas Python 2/3:
try:
reload
except NameError:
# Python 3
from imp import reload
Anda dapat menggunakannya seperti reload()
di kedua versi yang membuat semuanya lebih sederhana.
Jawaban yang diterima tidak menangani kasus dari X import Y. Kode ini menanganinya dan kasing standar juga:
def importOrReload(module_name, *names):
import sys
if module_name in sys.modules:
reload(sys.modules[module_name])
else:
__import__(module_name, fromlist=names)
for name in names:
globals()[name] = getattr(sys.modules[module_name], name)
# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")
Dalam kasus memuat ulang, kami menetapkan kembali nama tingkat atas ke nilai yang disimpan dalam modul yang baru dimuat, yang memperbaruinya.
>>> from X import Y
memuat ulang lakukan>>> __import__('X', fromlist='Y')
fromlist='*'
?
from
dalam laporan impor. Hanya paket.simbol yang import <package>
jelas dan eksplisit dalam kode. Sadarilah ini mungkin tidak selalu mungkin atau diinginkan. (Ini satu pengecualian: dari import print_function yang akan datang.)
foo = reload(foo); from foo import *
Ini adalah cara modern memuat ulang modul:
from importlib import reload
Jika Anda ingin mendukung versi Python yang lebih lama dari 3,5, coba ini:
from sys import version_info
if version_info[0] < 3:
pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
from imp import reload # Python 3.0 - 3.4
else:
from importlib import reload # Python 3.5+
Untuk menggunakannya, jalankan reload(MODULE)
, ganti MODULE
dengan modul yang ingin Anda muat ulang.
Misalnya, reload(math)
akan memuat ulang math
modul.
from importlib import reload
. Maka Anda bisa melakukannya reload(MODULE_NAME)
. Tidak perlu untuk fungsi ini.
modulereload(MODULE_NAME)
lebih jelas dari pada adil reload(MODULE_NAME)
dan memiliki peluang lebih rendah untuk konflik dengan fungsi lain.
Jika Anda tidak berada di server, tetapi sedang mengembangkan dan perlu sering memuat ulang modul, inilah tip yang bagus.
Pertama, pastikan Anda menggunakan shell IPython yang luar biasa , dari proyek Notebook Jupyter. Setelah menginstal Jupyter, Anda dapat memulainya dengan ipython
, atau jupyter console
, atau bahkan lebih baik jupyter qtconsole
,, yang akan memberi Anda konsol berwarna yang bagus dengan penyelesaian kode di OS apa pun.
Sekarang di shell Anda, ketik:
%load_ext autoreload
%autoreload 2
Sekarang, setiap kali Anda menjalankan skrip, modul Anda akan dimuat ulang.
Di luar itu 2
, ada opsi lain dari autoreload magic :
%autoreload
Reload all modules (except those excluded by %aimport) automatically now.
%autoreload 0
Disable automatic reloading.
%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.
%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.
Bagi mereka seperti saya yang ingin membongkar semua modul (saat menjalankan interpreter Python di bawah Emacs ):
for mod in sys.modules.values():
reload(mod)
Informasi lebih lanjut ada di Reload modul Python .
sys.modules.values()
ada dalam modul. Sebagai contoh: >>> ketik (sys.modules.values () [1]) <class 'email.LazyImporter'> Jadi, jika saya mencoba menjalankan kode yang terjatuh (Saya Tahu itu tidak dimaksudkan sebagai solusi praktis, cukup menunjukkan itu).
if mod and mod.__name__ != "__main__": imp.reload(mod)
Ciri-ciri Enthought memiliki modul yang bekerja cukup baik untuk ini. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
Ini akan memuat kembali modul apa pun yang telah diubah, dan memperbarui modul lain dan objek yang digunakan yang menggunakannya. Itu tidak bekerja sebagian besar waktu dengan __very_private__
metode, dan dapat tersedak warisan kelas, tapi itu menghemat waktu yang gila karena harus me-restart aplikasi host ketika menulis PyQt guis, atau hal-hal yang berjalan di dalam program seperti Maya atau Nuke. Itu tidak berhasil mungkin 20-30% dari waktu, tetapi masih sangat membantu.
Paket Enthought tidak memuat ulang file saat mereka berubah - Anda harus menyebutnya secara eksplisit - tetapi itu seharusnya tidak terlalu sulit untuk diterapkan jika Anda benar-benar membutuhkannya
Mereka yang menggunakan python 3 dan memuat ulang dari importlib.
Jika Anda memiliki masalah seperti itu tampaknya modul tidak memuat ulang ... Itu karena perlu beberapa waktu untuk mengkompilasi ulang pyc (hingga 60 detik). Saya menulis petunjuk ini hanya agar Anda tahu jika Anda pernah mengalami masalah seperti ini.
2018-02-01
foo
harus berhasil diimpor terlebih dahulu. from importlib import reload
, reload(foo)
31.5. importlib - Implementasi impor - dokumentasi Python 3.6.4
Pilihan lain. Lihat bahwa standar Python importlib.reload
hanya akan mengimpor kembali pustaka yang diteruskan sebagai argumen. Itu tidak akan memuat ulang perpustakaan yang diimpor lib Anda. Jika Anda mengubah banyak file dan memiliki paket yang agak rumit untuk diimpor, Anda harus melakukan pemuatan ulang yang dalam .
Jika Anda menginstal IPython atau Jupyter , Anda dapat menggunakan fungsi untuk memuat ulang semua lib secara mendalam:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Jika Anda tidak memiliki Jupyter, instal dengan perintah ini di shell Anda:
pip3 install jupyter
reload() argument must be module
. Saya menggunakan impor fungsi khusus dan sepertinya tidak berfungsi. Menggunakan modul bawaan tidak berfungsi. :-( itu buang-buang waktu me-reload iPython untuk setiap perubahan kecil yang saya lakukan pada kode saya ...
Solusi dari sebelumnya baik untuk hanya mendapatkan informasi reset, tetapi tidak akan mengubah semua referensi (lebih dari reload
tetapi kurang dari yang dibutuhkan). Untuk benar-benar mengatur semua referensi juga, saya harus pergi ke pengumpul sampah, dan menulis ulang referensi di sana. Sekarang ini berfungsi seperti pesona!
Perhatikan bahwa ini tidak akan berfungsi jika GC dimatikan, atau jika memuat ulang data yang tidak dipantau oleh GC. Jika Anda tidak ingin mengacaukan dengan GC, jawaban asli mungkin cukup untuk Anda.
Kode baru:
import importlib
import inspect
import gc
from weakref import ref
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
# First, log all the references before reloading (because some references may be changed by the reload operation).
module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)
new_module = importlib.reload(module)
_reset_item_recursively(module, module_tree, new_module)
def _update_referrers(item, new_item):
refs = gc.get_referrers(item)
weak_ref_item = ref(item)
for coll in refs:
if type(coll) == dict:
enumerator = coll.keys()
elif type(coll) == list:
enumerator = range(len(coll))
else:
continue
for key in enumerator:
if weak_ref_item() is None:
# No refs are left in the GC
return
if coll[key] is weak_ref_item():
coll[key] = new_item
def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
if grayed_out_item_ids is None:
grayed_out_item_ids = set()
item_tree = dict()
attr_names = set(dir(item)) - _readonly_attrs
for sub_item_name in attr_names:
sub_item = getattr(item, sub_item_name)
item_tree[sub_item_name] = [sub_item, None]
try:
# Will work for classes and functions defined in that module.
mod_name = sub_item.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
or isinstance(sub_item, EnumMeta):
continue
grayed_out_item_ids.add(id(sub_item))
item_tree[sub_item_name][1] = \
_get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)
return item_tree
def _reset_item_recursively(item, item_subtree, new_item):
# Set children first so we don't lose the current references.
if item_subtree is not None:
for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():
try:
new_sub_item = getattr(new_item, sub_item_name)
except AttributeError:
# The item doesn't exist in the reloaded module. Ignore.
continue
try:
# Set the item
_reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
except Exception as ex:
pass
_update_referrers(item, new_item)
Seperti yang ditulis dalam jawaban @ bobince, jika sudah ada referensi ke modul itu di modul lain (terutama jika itu diimpor dengan as
kata kunci seperti import numpy as np
), contoh itu tidak akan ditimpa.
Hal ini terbukti cukup bermasalah dengan saya ketika menerapkan tes yang diperlukan "bersih-batu tulis" keadaan modul konfigurasi, jadi saya telah menulis sebuah fungsi bernama reset_module
yang menggunakan importlib
's reload
fungsi dan secara rekursif menimpa semua atribut modul dideklarasikan ini. Telah diuji dengan Python versi 3.6.
import importlib
import inspect
from enum import EnumMeta
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
'__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
'__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
new_module = importlib.reload(module)
reset_items = set()
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
_reset_item_recursively(module, new_module, module.__name__, reset_items)
def _reset_item_recursively(item, new_item, module_name, reset_items=None):
if reset_items is None:
reset_items = set()
attr_names = set(dir(item)) - _readonly_attrs
for sitem_name in attr_names:
sitem = getattr(item, sitem_name)
new_sitem = getattr(new_item, sitem_name)
try:
# Set the item
setattr(item, sitem_name, new_sitem)
try:
# Will work for classes and functions defined in that module.
mod_name = sitem.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
or isinstance(sitem, EnumMeta): # Deal with enums
continue
reset_items.add(id(sitem))
_reset_item_recursively(sitem, new_sitem, module_name, reset_items)
except Exception as ex:
raise Exception(sitem_name) from ex
Catatan: Gunakan dengan hati-hati! Menggunakan ini pada modul non-periferal (modul yang mendefinisikan kelas yang digunakan secara eksternal, misalnya) dapat menyebabkan masalah internal dengan Python (seperti masalah pickling / un-pickling).
bagi saya untuk kasus Abaqus itu cara kerjanya. Bayangkan file Anda adalah Class_VerticesEdges.py
sys.path.append('D:\...\My Pythons')
if 'Class_VerticesEdges' in sys.modules:
del sys.modules['Class_VerticesEdges']
print 'old module Class_VerticesEdges deleted'
from Class_VerticesEdges import *
reload(sys.modules['Class_VerticesEdges'])
Saya mendapat banyak kesulitan mencoba memuat ulang sesuatu di dalam Sublime Text, tetapi akhirnya saya dapat menulis utilitas ini untuk memuat kembali modul pada Sublime Text berdasarkan kode yang sublime_plugin.py
digunakan untuk memuat kembali modul.
Di bawah ini menerima Anda untuk memuat ulang modul dari jalur dengan spasi di namanya, kemudian setelah memuat ulang Anda dapat mengimpor seperti biasa.
def reload_module(full_module_name):
"""
Assuming the folder `full_module_name` is a folder inside some
folder on the python sys.path, for example, sys.path as `C:/`, and
you are inside the folder `C:/Path With Spaces` on the file
`C:/Path With Spaces/main.py` and want to re-import some files on
the folder `C:/Path With Spaces/tests`
@param full_module_name the relative full path to the module file
you want to reload from a folder on the
python `sys.path`
"""
import imp
import sys
import importlib
if full_module_name in sys.modules:
module_object = sys.modules[full_module_name]
module_object = imp.reload( module_object )
else:
importlib.import_module( full_module_name )
def run_tests():
print( "\n\n" )
reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" )
reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" )
from .tests import semantic_linefeed_unit_tests
from .tests import semantic_linefeed_manual_tests
semantic_linefeed_unit_tests.run_unit_tests()
semantic_linefeed_manual_tests.run_manual_tests()
if __name__ == "__main__":
run_tests()
Jika Anda menjalankan untuk pertama kalinya, ini akan memuat modul, tetapi jika nanti Anda dapat kembali metode / fungsinya run_tests()
itu akan memuat kembali file tes. Dengan Sublime Text ( Python 3.3.6
) ini sering terjadi karena interpreternya tidak pernah ditutup (kecuali Anda me-restart Sublime Text, yaitu Python3.3
interpreter).
Cara lain bisa dengan mengimpor modul dalam suatu fungsi. Dengan cara ini ketika fungsi selesai modul mendapat sampah yang dikumpulkan.
sys.modules
.