Python 递归版本的';重新加载';
当我开发Python代码时,我通常在解释器中以一种特殊的方式进行测试。我将Python 递归版本的';重新加载';,python,python-module,python-import,Python,Python Module,Python Import,当我开发Python代码时,我通常在解释器中以一种特殊的方式进行测试。我将导入一些模块,测试它,找到一个bug,修复bug并保存,然后使用内置的重新加载功能来重新加载(一些模块),然后再次测试 但是,假设在某个模块中,我导入了一些其他模块,并且在测试某个模块时,我在某个其他模块中发现了一个bug并修复了它。现在调用reload(some\u模块)将不会递归地重新导入some\u other\u模块。我必须手动重新导入依赖项(通过执行类似于reload(some\u module.some\u o
导入一些模块
,测试它,找到一个bug,修复bug并保存,然后使用内置的重新加载
功能来重新加载(一些模块)
,然后再次测试
但是,假设在某个模块
中,我导入了一些其他模块,并且在测试某个模块
时,我在某个其他模块
中发现了一个bug并修复了它。现在调用reload(some\u模块)
将不会递归地重新导入some\u other\u模块
。我必须手动重新导入依赖项(通过执行类似于reload(some\u module.some\u other\u module)
的操作,或者导入some\u other\u module;reload(some\u other\u module)
,或者,如果我更改了一大堆依赖项并失去了需要重新加载的内容,我需要重新启动整个解释器
更方便的是如果有一些recursive\u reload
函数,我可以做recursive\u reload(some\u module)
,让Python不仅重新加载some\u module
,而且递归地重新加载some\u module
导入的每个模块(以及每个模块导入的每个模块,等等),这样我就可以确保我没有使用某个模块所依赖的任何其他模块的旧版本
我不认为Python中内置了任何类似于我在这里描述的
recursive\u reload
函数的功能,但是有没有一种简单的方法可以将这些功能组合在一起?编写一些测试用例并在每次修改模块时运行它们不是更简单吗
您所做的很酷(实际上您使用的是TDD(测试驱动开发),但您做得不对
通过编写单元测试(使用默认的python模块,或者更好)来考虑这一点与在交互环境中测试模块相比,您可以使用可重用、稳定的测试,帮助您更快更好地检测代码中的不一致性。这是一件棘手的事情-我有一个工作示例:
从技术上讲,您可以在每个文件中放置重新加载命令,以确保每次导入时都重新加载 a、 py: b、 py: 现在,以交互方式:
import b
b.testb()
#hi!
#<modify a.py>
reload(b)
b.testb()
#hello again!
导入b
b、 testb()
#嗨!
#
重新加载(b)
b、 testb()
#你好!
我遇到了同样的问题,你激励我去解决这个问题
from types import ModuleType
try:
from importlib import reload # Python 3.4+
except ImportError:
# Needed for Python 3.0-3.3; harmless in Python 2.7 where imp.reload is just an
# alias for the builtin reload.
from imp import reload
def rreload(module):
"""Recursively reload modules."""
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if type(attribute) is ModuleType:
rreload(attribute)
或者,如果您使用的是IPython,只需在启动时使用
dreload
或传递--deep reload
。我遇到过同样的问题,并且我已经建立了@Mattew和@osa answer
from types import ModuleType
import os, sys
def rreload(module, paths=None, mdict=None):
"""Recursively reload modules."""
if paths is None:
paths = ['']
if mdict is None:
mdict = {}
if module not in mdict:
# modules reloaded from this module
mdict[module] = []
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if type(attribute) is ModuleType:
if attribute not in mdict[module]:
if attribute.__name__ not in sys.builtin_module_names:
if os.path.dirname(attribute.__file__) in paths:
mdict[module].append(attribute)
rreload(attribute, paths, mdict)
reload(module)
#return mdict
有三个区别:
我发现redsk的答案非常有用。 我提出了一个简化版本(针对用户,而不是代码),其中模块的路径自动收集,递归适用于任意级别。 所有内容都包含在单个函数中。 在Python3.4上测试。我想Python3.3必须
从imp
导入重载,而不是从importlib导入重载。
它还检查\uuuuuuuuuuuuuuuu文件是否存在,如果编码器忘记在子模块中定义\uuuuuuuu init\uuuuuupy
文件,则该文件可能是错误的。在这种情况下,会引发异常
def rreload(module):
"""
Recursive reload of the specified module and (recursively) the used ones.
Mandatory! Every submodule must have an __init__.py file
Usage:
import mymodule
rreload(mymodule)
:param module: the module to load (the module itself, not a string)
:return: nothing
"""
import os.path
import sys
def rreload_deep_scan(module, rootpath, mdict=None):
from types import ModuleType
from importlib import reload
if mdict is None:
mdict = {}
if module not in mdict:
# modules reloaded from this module
mdict[module] = []
# print("RReloading " + str(module))
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
# print ("for attr "+attribute_name)
if type(attribute) is ModuleType:
# print ("typeok")
if attribute not in mdict[module]:
# print ("not int mdict")
if attribute.__name__ not in sys.builtin_module_names:
# print ("not a builtin")
# If the submodule is a python file, it will have a __file__ attribute
if not hasattr(attribute, '__file__'):
raise BaseException("Could not find attribute __file__ for module '"+str(attribute)+"'. Maybe a missing __init__.py file?")
attribute_path = os.path.dirname(attribute.__file__)
if attribute_path.startswith(rootpath):
# print ("in path")
mdict[module].append(attribute)
rreload_deep_scan(attribute, rootpath, mdict)
rreload_deep_scan(module, rootpath=os.path.dirname(module.__file__))
对于Python 3.6+,您可以使用:
from types import ModuleType
import sys
import importlib
def deep_reload(m: ModuleType):
name = m.__name__ # get the name that is used in sys.modules
name_ext = name + '.' # support finding sub modules or packages
def compare(loaded: str):
return (loaded == name) or loaded.startswith(name_ext)
all_mods = tuple(sys.modules) # prevent changing iterable while iterating over it
sub_mods = filter(compare, all_mods)
for pkg in sorted(sub_mods, key=lambda item: item.count('.'), reverse=True):
importlib.reload(sys.modules[pkg]) # reload packages, beginning with the most deeply nested
该代码对于导入的依赖项模块非常有效,就像导入另一个\u模块一样,但是当模块从另一个\u模块导入函数时,导入一些\u func
失败
我扩展了@redsk的答案,尝试对这些函数进行智能化。我还添加了一个黑名单,因为不幸的是,键入
和导入库
没有出现在系统中。内置模块名
(可能还有更多)。我还想防止重新加载我知道的一些依赖项
我还跟踪重新加载的模块名称并返回它们
在Python 3.7.4 Windows上测试:
def-rreload(模块,路径=None,MDCT=None,基本模块=None,黑名单=None,重新加载的模块=None):
“”“递归地重新加载模块。”“”
如果路径为“无”:
路径=[“”]
如果MDCT为无:
mdct={}
如果模块不在MDCT中:
#从此模块重新加载的模块
MDCT[模块]=[]
如果基本模块为无:
基本模块=模块
如果黑名单为无:
黑名单=[“导入库”,“键入”]
如果重新加载的_模块为无:
重新加载的_模块=[]
重新加载(模块)
重新加载的模块。追加(模块名称)
对于目录(模块)中的属性名称:
attribute=getattr(模块、属性名称)
如果类型(属性)为ModuleType and attribute.\uuuuu name\uuuuuu不在黑名单中:
如果属性不在MDCT[模块]中:
如果属性。\名称\不在sys.builtin\模块\名称中:
如果路径中的os.path.dirname(属性文件):
MDCT[模块].追加(属性)
重新加载的\u模块=重新加载(属性、路径、MDCT、基本\u模块、黑名单、重新加载的\u模块)
elif可调用(属性)和属性。模块不在黑名单中:
if属性。模块不在sys.builtin\u模块名称和f“{attribute.\uu”mo中
def rreload(module):
"""
Recursive reload of the specified module and (recursively) the used ones.
Mandatory! Every submodule must have an __init__.py file
Usage:
import mymodule
rreload(mymodule)
:param module: the module to load (the module itself, not a string)
:return: nothing
"""
import os.path
import sys
def rreload_deep_scan(module, rootpath, mdict=None):
from types import ModuleType
from importlib import reload
if mdict is None:
mdict = {}
if module not in mdict:
# modules reloaded from this module
mdict[module] = []
# print("RReloading " + str(module))
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
# print ("for attr "+attribute_name)
if type(attribute) is ModuleType:
# print ("typeok")
if attribute not in mdict[module]:
# print ("not int mdict")
if attribute.__name__ not in sys.builtin_module_names:
# print ("not a builtin")
# If the submodule is a python file, it will have a __file__ attribute
if not hasattr(attribute, '__file__'):
raise BaseException("Could not find attribute __file__ for module '"+str(attribute)+"'. Maybe a missing __init__.py file?")
attribute_path = os.path.dirname(attribute.__file__)
if attribute_path.startswith(rootpath):
# print ("in path")
mdict[module].append(attribute)
rreload_deep_scan(attribute, rootpath, mdict)
rreload_deep_scan(module, rootpath=os.path.dirname(module.__file__))
from types import ModuleType
import sys
import importlib
def deep_reload(m: ModuleType):
name = m.__name__ # get the name that is used in sys.modules
name_ext = name + '.' # support finding sub modules or packages
def compare(loaded: str):
return (loaded == name) or loaded.startswith(name_ext)
all_mods = tuple(sys.modules) # prevent changing iterable while iterating over it
sub_mods = filter(compare, all_mods)
for pkg in sorted(sub_mods, key=lambda item: item.count('.'), reverse=True):
importlib.reload(sys.modules[pkg]) # reload packages, beginning with the most deeply nested