Tôi có một máy chủ Python hoạt động lâu dài và muốn có thể nâng cấp dịch vụ mà không cần khởi động lại máy chủ. Cách tốt nhất để làm điều này là gì?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Tôi có một máy chủ Python hoạt động lâu dài và muốn có thể nâng cấp dịch vụ mà không cần khởi động lại máy chủ. Cách tốt nhất để làm điều này là gì?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Câu trả lời:
Bạn có thể tải lại một mô-đun khi nó đã được nhập bằng cách sử dụng reload
hàm dựng sẵn (chỉ dành cho Python 3,4+) :
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
Trong Python 3, reload
đã được chuyển đến imp
mô-đun. Trong 3,4, imp
đã không được ủng hộ importlib
và reload
được thêm vào sau này. Khi nhắm mục tiêu 3 trở lên, hãy tham chiếu mô-đun thích hợp khi gọi reload
hoặc nhập mô-đun .
Tôi nghĩ rằng đây là những gì bạn muốn. Các máy chủ web như máy chủ phát triển của Django sử dụng điều này để bạn có thể thấy tác động của các thay đổi mã của mình mà không cần khởi động lại quy trình máy chủ.
Để trích dẫn từ các tài liệu:
Mã của mô-đun Python được biên dịch lại và mã cấp mô-đun được kiểm tra lại, xác định một bộ đối tượng mới được liên kết với tên trong từ điển của mô-đun. Hàm init của các mô đun mở rộng không được gọi là lần thứ hai. Như với tất cả các đối tượng khác trong Python, các đối tượng cũ chỉ được thu hồi sau khi số tham chiếu của chúng giảm xuống không. Các tên trong không gian tên mô-đun được cập nhật để trỏ đến bất kỳ đối tượng mới hoặc thay đổi. Các tham chiếu khác đến các đối tượng cũ (chẳng hạn như tên bên ngoài mô-đun) không được bật lại để tham chiếu đến các đối tượng mới và phải được cập nhật trong mỗi không gian tên nơi chúng xảy ra nếu muốn.
Như bạn đã lưu ý trong câu hỏi của mình, bạn sẽ phải xây dựng lại Foo
các đối tượng nếu Foo
lớp nằm trong foo
mô-đun.
X
không phải là một mô-đun, bạn có thểimport sys; reload(sys.modules[X.__module__])
is_changed
hàm này chỉ là một hàm tùy ý mà bạn sẽ phải viết; nó không phải là một tích hợp. Ví dụ, nó có thể có thể mở tệp tương ứng với mô-đun mà bạn đang nhập và tìm khác với phiên bản được lưu trong bộ nhớ cache để xem nó có thay đổi không.
Trong Python 3.0 Ném3.3 bạn sẽ sử dụng: imp.reload(module)
Các BDFL đã trả lời câu hỏi này.
Tuy nhiên, imp
đã không được chấp nhận trong 3,4, ủng hộimportlib
(cảm ơn @Stefan! ).
Tôi nghĩ , do đó, bây giờ bạn sẽ sử dụng importlib.reload(module)
, mặc dù tôi không chắc chắn.
reload(__builtins__)
có hiệu lực trong 2.x
Có thể đặc biệt khó xóa một mô-đun nếu nó không phải là Python thuần túy.
Đây là một số thông tin từ: Làm thế nào để tôi thực sự xóa một mô-đun nhập khẩu?
Bạn có thể sử dụng sys.getrefcount () để tìm hiểu số lượng tài liệu tham khảo thực tế.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Các số lớn hơn 3 chỉ ra rằng sẽ khó thoát khỏi mô-đun. Mô-đun "trống" trong nhà (không chứa gì) nên được thu gom rác sau
>>> del sys.modules["empty"]
>>> del empty
vì tham chiếu thứ ba là một tạo phẩm của hàm getrefcount ().
setattr(package, "empty", None)
reload()
chỉ tải lại mô-đun cao nhất và mọi thứ bên trong nó sẽ không được tải lại trừ khi bạn xóa nó lần đầu tiên khỏi sys.modules.
reload(module)
, nhưng chỉ khi nó hoàn toàn độc lập. Nếu bất cứ điều gì khác có liên quan đến mô-đun (hoặc bất kỳ đối tượng nào thuộc mô-đun), thì bạn sẽ gặp các lỗi tinh vi và tò mò do mã cũ treo xung quanh lâu hơn bạn mong đợi và những thứ như isinstance
không hoạt động trên các phiên bản khác nhau của cùng mã.
Nếu bạn có phụ thuộc một chiều, bạn cũng phải tải lại tất cả các mô-đun phụ thuộc vào mô-đun được tải lại để thoát khỏi tất cả các tham chiếu đến mã cũ. Và sau đó tải lại các mô-đun phụ thuộc vào các mô-đun được tải lại, đệ quy.
Nếu bạn có các phụ thuộc vòng tròn, điều rất phổ biến, ví dụ như khi bạn đang xử lý tải lại một gói, bạn phải dỡ tất cả các mô-đun trong nhóm trong một lần. Bạn không thể làm điều này với reload()
vì nó sẽ nhập lại từng mô-đun trước khi các phụ thuộc của nó được làm mới, cho phép các tham chiếu cũ len vào các mô-đun mới.
Cách duy nhất để làm điều đó trong trường hợp này là hack sys.modules
, đây là loại không được hỗ trợ. Bạn phải trải qua và xóa từng sys.modules
mục bạn muốn được tải lại trong lần nhập tiếp theo và cũng xóa các mục có giá trị None
để xử lý vấn đề triển khai để thực hiện với bộ đệm tương đối không nhập. Nó không đẹp lắm nhưng miễn là bạn có một bộ phụ thuộc hoàn toàn khép kín mà không để các tham chiếu bên ngoài cơ sở mã của nó, thì nó hoàn toàn khả thi.
Có lẽ tốt nhất để khởi động lại máy chủ. :-)
None
các giá trị vì tôi đang xử lý chính xác vấn đề này: Tôi đang xóa các mục từ sys.modules
và sau khi nhập lại một số phụ thuộc đã nhập None
.
None
mục được quản lý để quay trở lại thông qua cơ chế nhập khi các mục 'thực' bị xóa và dường như tôi không thể thực hiện được vào ngày 2.7; trong tương lai chắc chắn nó không còn là vấn đề nữa vì hàng nhập khẩu tương đối đã biến mất. Trong khi đó, xóa tất cả các mục có None
giá trị dường như sẽ khắc phục nó.
reload
chức năng? Nó được tích hợp sẵn, bạn không phải nhập bất kỳ thư viện nào.
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
, ngay cả sau đó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]))
và mô-đun json vẫn hoạt động mặc dù nó không còn trong sys.modules nữa.
Đối với Python 2, sử dụng hàm tích hợp tải lại () :
reload(module)
Đối với Python 2 và 3.2, 3.13, sử dụng tải lại từ mô-đun imp :
import imp
imp.reload(module)
Nhưng imp
không được chấp nhận kể từ phiên bản 3.4 có lợi cho importlib , vì vậy hãy sử dụng:
import importlib
importlib.reload(module)
hoặc là
from importlib import reload
reload(module)
from six import reload_module
( pip install six
tất nhiên là trước tiên)
Đoạn mã sau cho phép bạn tương thích Python 2/3:
try:
reload
except NameError:
# Python 3
from imp import reload
Bạn có thể sử dụng nó như reload()
trong cả hai phiên bản, điều này làm cho mọi thứ đơn giản hơn.
Câu trả lời được chấp nhận không xử lý trường hợp X nhập Y. Mã này xử lý nó và trường hợp nhập tiêu chuẩn là tốt:
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")
Trong trường hợp tải lại, chúng tôi gán lại tên cấp cao nhất cho các giá trị được lưu trữ trong mô-đun mới được tải lại, cập nhật chúng.
>>> from X import Y
tải lại làm>>> __import__('X', fromlist='Y')
fromlist='*'
nào?
from
trong báo cáo nhập khẩu. Chỉ cần stark import <package>
và rõ ràng gói.symbol trong mã. Nhận ra điều này có thể không phải luôn luôn có thể hoặc mong muốn. (Đây là một ngoại lệ: từ print_feft nhập trong tương lai.)
foo = reload(foo); from foo import *
Đây là cách hiện đại để tải lại một mô-đun:
from importlib import reload
Nếu bạn muốn hỗ trợ các phiên bản Python cũ hơn 3,5, hãy thử điều này:
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+
Để sử dụng nó, hãy chạy reload(MODULE)
, thay thế MODULE
bằng mô-đun bạn muốn tải lại.
Ví dụ, reload(math)
sẽ tải lại math
mô-đun.
from importlib import reload
. Sau đó, bạn có thể làm reload(MODULE_NAME)
. Không cần cho chức năng này.
modulereload(MODULE_NAME)
là tự giải thích nhiều hơn chỉ reload(MODULE_NAME)
và có cơ hội xung đột với các chức năng khác thấp hơn.
Nếu bạn không ở trong một máy chủ, nhưng đang phát triển và cần thường xuyên tải lại một mô-đun, đây là một mẹo hay.
Trước tiên, hãy chắc chắn rằng bạn đang sử dụng trình bao IPython tuyệt vời , từ dự án Jupyter Notebook. Sau khi cài đặt Jupyter, bạn có thể khởi động nó ipython
, hoặc jupyter console
, hoặc thậm chí tốt hơn, jupyter qtconsole
sẽ cung cấp cho bạn một giao diện điều khiển được tô màu đẹp với hoàn thành mã trong bất kỳ HĐH nào.
Bây giờ trong vỏ của bạn, gõ:
%load_ext autoreload
%autoreload 2
Bây giờ, mỗi khi bạn chạy tập lệnh của mình, các mô-đun của bạn sẽ được tải lại.
Ngoài ra 2
, còn có các tùy chọn khác của phép thuật tự động tải :
%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.
Đối với những người như tôi muốn dỡ tất cả các mô-đun (khi chạy trong trình thông dịch Python trong Emacs ):
for mod in sys.modules.values():
reload(mod)
Thông tin thêm có trong Tải lại các mô-đun Python .
sys.modules.values()
đều là một mô-đun. Ví dụ: >>> type (sys.modules.values () [1]) <class 'email.LazyImporter'> Vì vậy, nếu tôi cố chạy mã đó, nó sẽ rơi xuống (Tôi biết nó không có nghĩa là một giải pháp thực tế, chỉ là chỉ ra rằng).
if mod and mod.__name__ != "__main__": imp.reload(mod)
Enth think Traits có một mô-đun hoạt động khá tốt cho việc này. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
Nó sẽ tải lại bất kỳ mô-đun nào đã được thay đổi, và cập nhật các mô-đun khác và các đối tượng được kích hoạt đang sử dụng nó. Nó không hoạt động hầu hết thời gian với __very_private__
các phương thức và có thể làm nghẹt kế thừa lớp, nhưng nó giúp tôi tiết kiệm thời gian điên rồ khi phải khởi động lại ứng dụng máy chủ khi viết guQ PyQt, hoặc những thứ chạy bên trong các chương trình như Maya hoặc Nuke. Nó không hoạt động có thể 20-30% thời gian, nhưng nó vẫn cực kỳ hữu ích.
Gói của Enth think không tải lại các tệp ngay khi chúng thay đổi - bạn phải gọi nó một cách rõ ràng - nhưng đó không phải là tất cả khó thực hiện nếu bạn thực sự cần nó
2018 / 02-01
foo
phải được nhập thành công trước. from importlib import reload
, reload(foo)
31,5. importlib - Việc thực hiện nhập - Tài liệu Python 3.6.4
Tùy chọn khác. Xem rằng mặc định Python importlib.reload
sẽ chỉ nhập lại thư viện được truyền dưới dạng đối số. Nó sẽ không tải lại các thư viện mà lib nhập của bạn. Nếu bạn đã thay đổi rất nhiều tệp và có một gói hơi phức tạp để nhập, bạn phải tải lại sâu .
Nếu bạn đã cài đặt IPython hoặc Jupyter , bạn có thể sử dụng một chức năng để tải lại sâu tất cả các lib:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Nếu bạn không có Jupyter, hãy cài đặt nó bằng lệnh này trong trình bao của bạn:
pip3 install jupyter
reload() argument must be module
. Tôi đang sử dụng chức năng nhập tùy chỉnh và dường như không hoạt động. Sử dụng các mô-đun tích hợp không hoạt động. :-( thật lãng phí thời gian khi tải lại iPython cho mỗi thay đổi nhỏ mà tôi đã thực hiện đối với mã của mình ...
Giải pháp từ trước là tốt cho việc chỉ lấy thông tin đặt lại, nhưng nó sẽ không thay đổi tất cả các tham chiếu (nhiều hơn reload
nhưng ít hơn yêu cầu). Để thực sự thiết lập tất cả các tài liệu tham khảo, tôi đã phải đi vào trình thu gom rác và viết lại các tài liệu tham khảo ở đó. Bây giờ nó hoạt động như một nét duyên dáng!
Lưu ý rằng điều này sẽ không hoạt động nếu tắt GC hoặc nếu tải lại dữ liệu không được giám sát bởi GC. Nếu bạn không muốn gây rối với GC, câu trả lời ban đầu có thể là đủ cho bạn.
Mã mới:
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)
Như được viết trong câu trả lời của @ bobince, nếu đã có một tham chiếu đến mô-đun đó trong một mô-đun khác (đặc biệt là nếu nó được nhập với as
từ khóa như import numpy as np
), thì trường hợp đó sẽ không bị ghi đè.
Điều này tỏ ra khá khó khăn với tôi khi áp dụng các thử nghiệm yêu cầu trạng thái "sạch sẽ" của các mô-đun cấu hình, vì vậy tôi đã viết một hàm có tên reset_module
là sử dụng chức năng importlib
của reload
nó và ghi đè lên tất cả các thuộc tính của mô-đun được khai báo. Nó đã được thử nghiệm với phiên bản Python 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
Lưu ý: Sử dụng cẩn thận! Việc sử dụng chúng trên các mô-đun không ngoại vi (ví dụ mô-đun xác định các lớp được sử dụng bên ngoài) có thể dẫn đến các vấn đề nội bộ trong Python (chẳng hạn như các vấn đề tẩy / không tẩy).
Đối với tôi đối với trường hợp của Abaqus, đó là cách nó hoạt động. Hãy tưởng tượng tệp của bạn là 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'])
Tôi đã gặp nhiều rắc rối khi cố tải lại một cái gì đó bên trong Sublime Text, nhưng cuối cùng tôi có thể viết tiện ích này để tải lại các mô-đun trên Sublime Text dựa trên mã sublime_plugin.py
sử dụng để tải lại các mô-đun.
Điều này dưới đây chấp nhận bạn tải lại các mô-đun từ các đường dẫn có khoảng trắng trên tên của chúng, sau đó sau khi tải lại, bạn có thể nhập như bạn thường làm.
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()
Nếu bạn chạy lần đầu tiên, điều này sẽ tải mô-đun, nhưng nếu sau này bạn có thể lại phương thức / chức năng run_tests()
thì nó sẽ tải lại các tệp kiểm tra. Với Sublime Text ( Python 3.3.6
) điều này xảy ra rất nhiều vì trình thông dịch của nó không bao giờ đóng (trừ khi bạn khởi động lại Sublime Text, tức là trình Python3.3
thông dịch).
Một cách khác có thể là nhập mô-đun trong một chức năng. Cách này khi chức năng hoàn thành mô-đun được thu gom rác.
sys.modules
.