Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 从模块导入反转*_Python_Namespaces_Python Import - Fatal编程技术网

Python 从模块导入反转*

Python 从模块导入反转*,python,namespaces,python-import,Python,Namespaces,Python Import,我有一个代码库,我正在清理前一个开发人员的一些混乱决策。他经常做类似的事情: from scipy import * from numpy import * …当然,这会污染名称空间,并且很难判断模块中的属性最初来自何处 有没有办法让Python为我分析并修复这个问题?有人为此做了一个实用程序吗?如果没有,如何制作这样的实用程序?好的,这就是我认为你可以做的,破坏程序。删除导入并注意所产生的错误。然后只导入你想要的模块,这可能需要一段时间,但这是我知道的唯一方法,如果有人知道一个工具来帮助我,

我有一个代码库,我正在清理前一个开发人员的一些混乱决策。他经常做类似的事情:

from scipy import *
from numpy import *
…当然,这会污染名称空间,并且很难判断模块中的属性最初来自何处


有没有办法让Python为我分析并修复这个问题?有人为此做了一个实用程序吗?如果没有,如何制作这样的实用程序?

好的,这就是我认为你可以做的,破坏程序。删除导入并注意所产生的错误。然后只导入你想要的模块,这可能需要一段时间,但这是我知道的唯一方法,如果有人知道一个工具来帮助我,我会很高兴地感到惊讶

编辑:
啊,是的,一根短绒,我没想到。

是的。移除导入并在模块上运行一个过梁

我建议使用,尽管它也可能会产生很多关于样式错误的噪音

仅仅删除导入并尝试运行代码可能是不够的,因为只有使用正确的输入运行正确的代码行,才能引发许多名称错误。linter将通过解析来分析代码,并检测潜在的
namererror
s,而无需运行代码

这一切都假定没有可靠的单元测试,或者测试没有提供足够的覆盖率

在这种情况下,如果有多行来自moduleimport*的
,那么您需要为每个缺少的名称找出哪个模块提供了该名称,这就有点麻烦了。这需要手动操作,但您只需在python解释器中导入模块并测试该模块上是否定义了缺少的名称:

>>> import scipy, numpy
>>> 'loadtxt' in dir(numpy)
True
您确实需要考虑在这种特定情况下,
numpy
scipy
模块之间存在重叠;对于两个模块中定义的任何名称,该模块上次导入wins


请注意,将模块导入*
中的任何
行保留在适当位置意味着linter将无法检测到哪些名称可能会引发名称错误

我认为PurityLake和Martijn Pieters的辅助手动解决方案可能是最好的选择。但以编程方式实现这一点并非不可能

首先,您需要获取模块字典中可能用于代码的所有名称的列表。我假设您的代码没有直接调用任何dunder函数等

然后,您需要使用
inspect.getmodule()
对它们进行迭代,以找出每个对象最初是在哪个模块中定义的。我假设您没有使用任何从foo import*
-ed双重定义的
。列出
numpy
scipy
模块中定义的所有名称

现在,您可以获取该输出并将每个
foo
替换为
numpy.foo

所以,把它放在一起,像这样:

for modname in sys.argv[1:]:
    with open(modname + '.py') as srcfile:
        src = srcfile.read()
    src = src.replace('from numpy import *', 'import numpy')
    src = src.replace('from scipy import *', 'import scipy')
    mod = __import__(modname)
    for name in dir(mod):
        original_mod = inspect.getmodule(getattr(mod, name))
        if original_mod.__name__ == 'numpy':
            src = src.replace(name, 'numpy.'+name)
        elif original_mod.__name__ == 'scipy':
            src = src.replace(name, 'scipy.'+name)
    with open(modname + '.tmp') as dstfile:
        dstfile.write(src)
    os.rename(modname + '.py', modname + '.bak')
    os.rename(modname + '.tmp', modname + '.py')

如果其中任何一个假设都是错误的,那么修改代码就不难了。此外,您可能希望使用
tempfile.NamedTemporaryFile
和其他改进,以确保不会意外地用临时文件覆盖内容。(我只是不想处理编写跨平台内容的头痛问题;如果你没有在Windows上运行,这很容易。)并添加一些错误处理,很明显,可能还有一些报告。

我现在制作了一个小实用程序,我称之为“dedazzler”。它将查找“来自模块导入*”的行,然后展开目标模块的“目录”,替换这些行

在运行它之后,您仍然需要运行一个linter。下面是代码中特别有趣的部分:

import re

star_match = re.compile('from\s(?P<module>[\.\w]+)\simport\s[*]')
now = str(time.time())
error = lambda x: sys.stderr.write(x + '\n')

def replace_imports(lines):
    """
    Iterates through lines in a Python file, looks for 'from module import *'
    statements, and attempts to fix them.
    """
    for line_num, line in enumerate(lines):
        match = star_match.search(line)
        if match:
            newline = import_generator(match.groupdict()['module'])
            if newline:
                lines[line_num] = newline
    return lines

def import_generator(modulename):
    try:
        prop_depth = modulename.split('.')[1:]
        namespace = __import__(modulename)
        for prop in prop_depth:
            namespace = getattr(namespace, prop)
    except ImportError:
        error("Couldn't import module '%s'!" % modulename)
        return
    directory = [ name for name in dir(namespace) if not name.startswith('_') ]
    return "from %s import %s\n"% (modulename, ', '.join(directory))
重新导入
star\u match=re.compile('from\s(?P[\.\w]+)\simport\s[*]))
now=str(time.time())
错误=lambda x:sys.stderr.write(x+'\n')
def替换_导入(行):
"""
遍历Python文件中的行,查找“from module import*”
语句,并尝试修复它们。
"""
对于行数,枚举中的行(行):
匹配=星号匹配。搜索(行)
如果匹配:
换行符=导入\u生成器(match.groupdict()['module'])
如果换行:
行[行数]=换行
回程线
def导入_生成器(模块名称):
尝试:
prop_depth=modulename.split('.')[1:]
名称空间=\uuuu导入\uuuu(模块名称)
对于支柱深度中的支柱:
namespace=getattr(名称空间,属性)
除恐怖外:
错误(“无法导入模块“%s!”%modulename)
返回
directory=[dir(名称空间)中名称的名称(如果不是name.startswith(“”“)]
返回“从%s导入%s\n”%(modulename,,”。加入(目录))
我在这里以一种更有用的独立实用程序形式维护它:


我同情你。希望你能找到一些好工具。(+1)更好的是,我希望你写一个不错的工具(无论是否基于我的答案),并将其发布在PyPI上,这样如果我需要这样的东西,我就不必自己做了。:)还可以看到这个问题:如果我删除导入,这将如何告诉我名称来自哪个模块?是的,你必须弄清楚每个属性应该来自哪个模块。幸运的是,这并不是那么难。如果你一次删除一个,这可能会更容易。我应该先运行linter以获得它可能发现的任何一般问题,解决它们,删除一个导入行,再次运行它以查看哪些名称作为名称错误弹出,然后将它们添加到“from”导入,然后在每次通配符导入时重复这个过程?@MartijnPieters:这实际上会令人惊讶地痛苦。以OP的scipy import*
中的
和numpy import*
中的
为例,一些函数的语义,例如
log10
将取决于哪个imp