Uzun süredir çalışan bir Python sunucum var ve sunucuyu yeniden başlatmadan bir hizmeti yükseltmek istiyorum. Bunu yapmanın en iyi yolu nedir?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Uzun süredir çalışan bir Python sunucum var ve sunucuyu yeniden başlatmadan bir hizmeti yükseltmek istiyorum. Bunu yapmanın en iyi yolu nedir?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Yanıtlar:
Yerleşikreload
işlevi kullanarak önceden alınmışsa bir modülü yeniden yükleyebilirsiniz (yalnızca Python 3.4+) :
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
Python 3'te, modüle reload
taşındı imp
. 3.4 yılında imp
lehine kaldırıldı importlib
ve reload
ikincisi eklendi. 3 veya daha üstünü hedeflerken, arama yaparken ilgili modüle başvurun reload
veya içe aktarın.
Bence istediğin bu. Django'nun geliştirme sunucusu gibi web sunucuları, sunucu işleminin kendisini yeniden başlatmadan kod değişikliklerinizin etkilerini görebilmeniz için bunu kullanır.
Dokümanlardan alıntı yapmak için:
Python modüllerinin kodu yeniden derlenir ve modül düzeyindeki kod yeniden verilir, böylece modül sözlüğündeki isimlere bağlı yeni bir nesne kümesi tanımlanır. Genişletme modüllerinin başlatma işlevi ikinci kez çağrılmaz. Python'daki diğer tüm nesnelerde olduğu gibi, eski nesneler yalnızca referans sayıları sıfıra düştükten sonra geri kazanılır. Modül ad alanındaki adlar yeni veya değiştirilmiş herhangi bir nesneyi işaret edecek şekilde güncellenir. Eski nesnelere (modüle harici adlar gibi) yapılan diğer başvurular, yeni nesnelere başvurmak için geri yüklenmez ve istenirse oluştukları her ad alanında güncellenmelidir.
Sorunuzda belirttiğiniz gibi Foo
, Foo
sınıf foo
modülde bulunuyorsa nesneleri yeniden yapılandırmanız gerekir .
X
bir modül değilse, şunları yapabilirsinizimport sys; reload(sys.modules[X.__module__])
is_changed
fonksiyonun yazmak zorunda olacağınız keyfi bir fonksiyon olduğundan eminim ; yerleşik değildir. Örneğin, içe aktardığınız modüle karşılık gelen dosyayı açabilir ve değişip değişmediğini görmek için önbelleğe alınmış bir sürümle farklılaştırabilir.
Python 3.0-3.3'te şunları kullanırsınız: imp.reload(module)
BDFL gelmiştir cevap bu soruyu.
Bununla birlikte, imp
3.4 yılındaimportlib
(teşekkürler @Stefan! ) Lehine önerilmemiştir .
Ben düşünüyorum bu nedenle, artık kullanıma ediyorum importlib.reload(module)
emin değilim ancak.
reload(__builtins__)
2.x için geçerlidir
Saf Python değilse bir modülü silmek özellikle zor olabilir.
Bazı bilgiler: İçe aktarılan bir modülü gerçekten nasıl silebilirim?
Gerçek referans sayısını bulmak için sys.getrefcount () kullanabilirsiniz.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
3'ten büyük sayılar modülden kurtulmanın zor olacağını gösterir. Evde yetiştirilen "boş" (hiçbir şey içermeyen) modül,
>>> del sys.modules["empty"]
>>> del empty
üncü referans ise getrefcount () işlevinin bir ürünüdür.
setattr(package, "empty", None)
reload()
yalnızca en üstteki modülü yeniden yükler ve sys.modules'den silmediğiniz sürece içindeki hiçbir şey yeniden yüklenmez.
reload(module)
ancak yalnızca tamamen bağımsızsa. Modüle (veya modüle ait herhangi bir nesneye) başka bir şey referansı varsa, eski kodun beklediğinizden daha uzun süre asılı kalması ve isinstance
farklı sürümlerde çalışmama gibi şeyler gibi ince ve meraklı hatalar alırsınız . aynı kod.
Tek yönlü bağımlılıklarınız varsa, eski koda yapılan tüm başvurulardan kurtulmak için yeniden yüklenen modüle bağlı olan tüm modülleri de yeniden yüklemeniz gerekir. Ve sonra tekrar yüklenen modüllere bağlı olan modülleri tekrar tekrar yükleyin.
Örneğin, bir paketi yeniden yüklemekle uğraşırken çok yaygın olan dairesel bağımlılıklarınız varsa, gruptaki tüm modülleri tek seferde boşaltmanız gerekir. reload()
Bağımlılıkları yenilenmeden önce her modülü yeniden içe aktaracak ve eski referansların yeni modüllere girmesine izin verecek şekilde bunu yapamazsınız .
Bu durumda bunu yapmanın tek yolu sys.modules
, bir tür desteklenmeyen hack'lemektir. Bir sys.modules
sonraki içe aktarma işleminde yeniden yüklenmesini istediğiniz her girdiyi incelemeniz ve silmeniz ve ayrıca, None
başarısız göreli içe aktarmaların önbelleğe alınmasıyla ilgili bir uygulama sorunuyla ilgilenecek girdileri silmeniz gerekir . Çok güzel değil, ancak referansları kod tabanının dışında bırakmayan tamamen bağımsız bir bağımlılık kümeniz olduğu sürece, uygulanabilir.
Muhtemelen sunucuyu yeniden başlatmak en iyisidir. :-)
None
Bu soruna tam olarak çalıştığım için değeri değerlere ayırabilir misiniz : sys.modules
Bazı içe aktarılan bağımlılıklar yeniden içe aktarıldıktan sonra ve sonra öğeleri siliyorum None
.
None
girişler 'gerçek' girişler silindiğinde ithalat mekanizması üzerinden geri yol başardı nasıl belli değil, ve bunu 2.7 yapmak mümkün olamaz; gelecekte örtük göreceli ithalatlar ortadan kalktığından, bu kesinlikle bir sorun olmaktan çıkmıştır. Bu arada, tüm girişlerin None
değerle silinmesi sorunu çözüyor gibi görünüyor.
reload
işlevi mi demek istediniz ? Yerleşik, herhangi bir kitaplığı içe aktarmak zorunda değilsiniz.
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
sonra bile kaldıreload(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]))
ve json modülü artık sys.modules'de olmasa bile hala çalışıyor.
Python 2 için yerleşik yeniden işlev () işlevini kullanın :
reload(module)
Python 2 ve 3.2-3.3 için imp modülünden yeniden yükle'yi kullanın :
import imp
imp.reload(module)
Ancak importlib lehine sürüm 3.4'ten beri imp
kullanımdan kaldırılmıştır , bu nedenle şunu kullanın:
import importlib
importlib.reload(module)
veya
from importlib import reload
reload(module)
from six import reload_module
( pip install six
önce elbette gerekir )
Aşağıdaki kod Python 2/3 uyumluluğuna izin verir:
try:
reload
except NameError:
# Python 3
from imp import reload
Her reload()
iki versiyonda olduğu gibi kullanabilirsiniz, bu da işleri kolaylaştırır.
Kabul edilen cevap, X içe aktarma Y durumuyla ilgilenmez. Bu kod, onu ve standart içe aktarma durumunu da ele alır:
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")
Yeniden yükleme durumunda, en üst düzey adları yeniden yüklenen yeni yüklenen modülde depolanan değerlere yeniden atarız.
>>> from X import Y
yeniden yüklemek>>> __import__('X', fromlist='Y')
fromlist='*'
mı?
from
, ithalat beyanlarında neredeyse tüm kullanımını durdurma eğilimindeyim . Kodda sade import <package>
ve açık package.symbol. Bunun her zaman mümkün veya arzu edilmeyebileceğini anlayın. (İşte bir istisna: Gelecekteki ithalat print_function'dan.)
foo = reload(foo); from foo import *
Bir modülü yeniden yüklemenin modern yolu:
from importlib import reload
3.5'ten eski Python sürümlerini desteklemek istiyorsanız, şunu deneyin:
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+
Kullanmak için, çalıştırın reload(MODULE)
, değiştirinMODULE
yeniden yüklemek istediğiniz modül ile .
Örneğin reload(math)
, math
modülü yeniden yükleyecektir.
from importlib import reload
. Sonra yapabilirsin reload(MODULE_NAME)
. Bu işleve gerek yoktur.
modulereload(MODULE_NAME)
kendinden daha açıklayıcı olduğuna inanıyorum reload(MODULE_NAME)
ve diğer işlevlerle çatışma şansı daha düşük.
Eğer varsa değil bir sunucuda, ancak gelişmekte ve sık sık bir modül yeniden ihtiyaç Burada güzel bir ipucu.
İlk olarak, Jupyter Notebook projesinden mükemmel IPython kabuğunu kullandığınızdan emin olun . Jupyter yükledikten sonra, birlikte başlatabilirsiniz ipython
veya, jupyter console
ya da daha da iyisi,jupyter qtconsole
size herhangi OS kod tamamlama ile güzel colorized konsolu verecek olan.
Şimdi kabuğunuza şunu yazın:
%load_ext autoreload
%autoreload 2
Şimdi, her seferinde komut dosyanızı çalıştırdığınızda, modülleriniz yeniden yüklenecektir.
Bunun ötesinde, otomatik yeniden yükleme sihrinin2
başka seçenekleri de vardır :
%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.
Benim gibi tüm modülleri boşaltmak isteyenler için ( Emacs altındaki Python yorumlayıcısında çalışırken ):
for mod in sys.modules.values():
reload(mod)
Daha fazla bilgi Yeniden Yükleme Python modülleri .
sys.modules.values()
bir modül değil. Örneğin: >>> type (sys.modules.values () [1]) <class 'email.LazyImporter'> Yani bu kodu çalıştırmayı denersem devrilir (bunun pratik bir çözüm olmadığını düşünüyorum, sadece işaret ediyor).
if mod and mod.__name__ != "__main__": imp.reload(mod)
Enthought Traits bunun için oldukça iyi çalışan bir modüle sahiptir. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
Değiştirilen tüm modülleri yeniden yükler ve onu kullanan diğer modülleri ve örnek nesneleri günceller. Çoğu zaman __very_private__
yöntemlerle çalışmaz ve sınıf mirasını boğabilir, ancak PyQt kılavuzu veya Maya veya Nuke gibi programların içinde çalışan şeyler yazarken ana bilgisayar uygulamasını yeniden başlatmak zorunda kalmamdan çılgınca zaman kazandırır. Belki% 20-30 oranında işe yaramaz, ancak yine de inanılmaz derecede yararlıdır.
Enthought'un paketi, değiştirildikleri anda dosyaları yeniden yüklemez - bunu açıkça çağırmanız gerekir - ancak gerçekten ihtiyacınız varsa uygulamak o kadar da zor olmamalıdır
2018/02/01
foo
önceden başarıyla içe aktarılması gerekir. from importlib import reload
, reload(foo)
31.5. importlib - İthalat uygulaması - Python 3.6.4 belgeleri
Diğer seçenek. Python varsayılanının importlib.reload
yalnızca argüman olarak geçirilen kütüphaneyi yeniden içe aktardığını görün. Bu olmaz sizin lib ithalat olduğunu kütüphaneleri yeniden yükleyin. Çok sayıda dosyayı değiştirdiyseniz ve içe aktarmak için biraz karmaşık bir paketiniz varsa, derin bir yeniden yükleme yapmanız gerekir .
Eğer varsa ipython veya Jupyter yüklü, derin yeniden yükleme tüm kütüphaneleri için bir işlevi kullanabilirsiniz:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Jupyter'iniz yoksa, kabuğunuzda bu komutla yükleyin:
pip3 install jupyter
reload() argument must be module
. Özel bir işlev içe aktarma kullanıyorum ve çalışmıyor gibi görünüyor. Yerleşik modülleri kullanmak işe yarar. :-(
Önceden gelen çözüm, sadece sıfırlama bilgilerini almak için iyidir, ancak tüm referansları değiştirmez (gerektiğinden daha az reload
ancak daha az). Aslında tüm referansları ayarlamak için, çöp toplayıcısına gitmem ve referansları yeniden yazmam gerekiyordu. Şimdi bir cazibe gibi çalışıyor!
GC kapalıysa veya GC tarafından izlenmeyen verileri yeniden yüklerseniz bunun işe yaramayacağını unutmayın . GC ile uğraşmak istemiyorsanız, orijinal cevap sizin için yeterli olabilir.
Yeni kod:
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)
@ Bobince'nin cevabında yazıldığı gibi, başka bir modülde bu modüle zaten bir referans varsa (özellikle as
benzer anahtar kelimeyle içe aktarılmışsa import numpy as np
), bu örneğin üzerine yazılmaz.
Yapılandırma modüllerinin "temiz kayrak" durumu gerektiren testleri uygularken bu benim için oldukça sorunlu oldu, bu yüzden 's işlevini reset_module
kullanan ve bildirilen tüm modül özniteliklerini tekrar tekrar üzerine yazan bir işlev yazdım . Python sürüm 3.6 ile test edilmiştir.importlib
reload
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
Not: Dikkatli kullanın! Bunları çevresel olmayan modüllerde (örneğin harici olarak kullanılan sınıfları tanımlayan modüller) kullanmak Python'da dahili sorunlara neden olabilir (dekapaj / dekapaj sorunları).
Abaqus davası için benim için böyle çalışıyor. Dosyanızın Class_VerticesEdges.py olduğunu düşünün
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'])
Sublime Text içindeki bir şeyi yeniden yüklemeye çalışırken çok fazla sorunum var, ancak nihayet Sublime Text modüllerini yeniden yüklemek için kullanılan kodlara dayanarak modülleri yeniden yüklemek için bu yardımcı programı yazabilirim sublime_plugin.py
.
Bu aşağıda, adları üzerinde boşluk bulunan yollardan modülleri yeniden yüklemenizi kabul eder, daha sonra yeniden yükledikten sonra genellikle yaptığınız gibi içe aktarabilirsiniz.
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()
İlk kez çalıştırırsanız, bu modülü yüklemelidir, ancak daha sonra yöntemi / işlevi tekrar yapabiliyorsanız run_tests()
, test dosyalarını yeniden yükleyecektir . Sublime Text ( Python 3.3.6
) ile bu çok fazla olur çünkü tercümanı asla kapanmaz (Sublime Text'i yeniden başlatmazsanız, yani Python3.3
tercüman).
Başka bir yol, modülü bir işlevde içe aktarmak olabilir. Bu şekilde, modül tamamlandığında çöp toplanır.
sys.modules
.