如何导入python中的模块
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何导入python中的模块相关的知识,希望对你有一定的参考价值。
参考技术A 定义模块,只要使用文本编辑器,把一些python代码输入到文本中,然后以.py为后缀名进行保存,任何此类文件都会被认为是python模块。比如说,下面的代码输入到一个文件中,就可以看作是一个模块:
def
printme(var):
varif
__name__
==
'__main__':
printme(1)
假设说输入到a.py中,那么import
a就可以把这个模块导入。
然后可执行a.printme(3),屏幕即可打印出3:
>>>
a.printme(3)3>>>
一个模块顶层定义的变量,会自动变成模块的属性。例如:
data=[1,2,3]def
printme(var):
varif
__name__
==
'__main__':
printme(1)
data变量就是模块的一个属性。其实printme也是一个属性,只不过是一个函数罢了。
引入模块示例如下:(假定此时data=[1,2,3]未定义)
>>>
import
a>>>
a.data
Traceback
(most
recent
call
last):
File
"<pyshell#1>",
line
1,
in
<module>
a.dataAttributeError:
'module'
object
has
no
attribute
'data'>>>
reload(a)<module
'a'
from
'C:/py\a.pyc'>>>>
a.data
Traceback
(most
recent
call
last):
File
"<pyshell#3>",
line
1,
in
<module>
a.dataAttributeError:
'module'
object
has
no
attribute
'data'>>>
从上述提示可以看出data属性未定义,此时再在a.py文件中定义data=[1,2,3],重新加载a模块,并输出data属性:
>>>
reload(a)<module
'a'
from
'C:/py\a.py'>>>>
a.data[1,
2,
3]>>>
这里的reload函数可以重新加载一个模块。如果在模块代码中更改了,那么需要重新加载。
上面a.data,就是访问模块中的属性。
上面的例子是导入一个文件作为一个模块。
其实python的模块导入还有更丰富的内容。
除了模块名之外,python也可以导入指定目录路径。python代码的目录就称为包。因此,这类导入就称为包导入。事实上,包导入是把计算机上的目录变成python的一个命名空间。而属性就是目录中包含的子目录或者是模块文件。
看下面例子:
在我的桌面上有一个aa文件夹,里面有bb文件夹,bb里面有a.py这个文件。
那么在aa和bb文件夹中分别放置一个__init__.py,之后,在命令行中import
aa.bb.a,就可以导入模块a了。
如何检查 Python 包中的任何模块是不是从另一个包导入?
【中文标题】如何检查 Python 包中的任何模块是不是从另一个包导入?【英文标题】:How to check if any module in a Python package imports from another package?如何检查 Python 包中的任何模块是否从另一个包导入? 【发布时间】:2022-01-20 16:10:03 【问题描述】:我想确保一个包 ("pkg-foo
") 中的所有模块不会从另一个包 ("pkg-block
") 导入。
更新:我知道由于 Python 的活力,有许多黑魔法方法可以导入模块。但是,我只对检查显式导入感兴趣(例如 import pkg.block
或 from pkg.block import ...
)。
我想通过pkg-foo
中的单元测试来强制执行此操作,以确保它永远不会从pkg-block
导入。
我怎样才能做到这一点?我使用 Python 3.8+,并希望使用内置插件或 setuptools
。
目前的半生不熟的解决方案
# pkg_resources is from setuptools
from pkg_resources import Distribution, working_set
# Confirm pgk-block is not in pkg-foo's install_requires
foo_pkg: Distribution = working_set.by_key[f"foo-pkg"]
for req in foo_pkg.requires():
assert "pkg-block" not in str(req)
然而,仅仅因为pkg-block
没有在setup.py
的install_requires
中声明,并不意味着它没有在包中导入。所以,这只是一个半生不熟的解决方案。
我的想法是我需要爬取pkg-foo
中的所有模块,并检查每个模块是否从pgk-block
导入。
【问题讨论】:
由于 Python 的灵活性,有很多方法可以导入模块。最后你不能完全确定,只有合理确定。 @MichaelButscher 我已经更新了问题以解决要检查哪些类型的进口,感谢您指出这一点 【参考方案1】:我认为在您的情况下最简单的方法是模拟不应导入的模块,即pkg-block
。为了模拟一个模块,把它放在sys.path
的开头,可能最简单的方法是在项目文件夹中创建一个假模块(或临时替换原始模块),所以它最终会在你的@ 987654323@立即。这会将所有导入“重定向”到您的模拟模块。在模拟模块本身中放置如下内容:
print(f'Module __name__ imported')
def __getattr__(name):
print(f'Imported attribute name from module __name__')
上面的代码将打印一行通知您导入了模块或其属性,这将适用于直接模块导入和from
导入、函数内部导入、importlib
导入(通过路径导入除外,但我觉得这太深奥了)。为了简化单元测试中对导入的检测,您可能想要引发异常而不是仅仅打印。您也不必进行特定测试来验证没有发生导入,只需运行 pkg-foo
的常规测试套件,看看是否有任何测试由于特定于导入的异常而失败。
示例:
假设文件结构如下:
|---main.py
|
\---pkg
|---block.py
|---foo.py
|---__init__.py
在哪里pkg/block.py
print(f'Module __name__ imported')
def __getattr__(name):
print(f'Imported attribute name from module __name__')
pkg/foo.py
import importlib
import pkg.block
from . import block
from .block import abc
importlib.import_module('pkg.block')
def from_function():
from pkg.block import from_function
main.py
import pkg.foo
if __name__ == '__main__':
pkg.foo.from_function()
那么在执行main.py
之后你会得到:
Module pkg.block imported
Imported attribute __path__ from module pkg.block
Imported attribute abc from module pkg.block
Imported attribute abc from module pkg.block
Imported attribute __path__ from module pkg.block
Imported attribute from_function from module pkg.block
Imported attribute from_function from module pkg.block
【讨论】:
【参考方案2】:所以我的建议是从概念上把这个问题分成两部分。
第一个子问题:确定pkg-foo
中导入的所有模块。让我们使用mod_foo
作为pkg-foo
中的任意导入模块
第二个子问题:确定是否有任何mod_foo
来自pkg-block
。如果这些模块都不在pkg-block
中,则通过单元测试,否则,单元测试失败。
要解决第一个子问题,您可以使用 modulefinder.ModuleFinder
类。如the example from the documentation所示,可以为pkg-foo
中的每个模块做modulefinder.ModuleFinder.run_script(pathname)
。然后,您可以通过从字典modulefinder.ModuleFinder.modules
中获取键来获取模块名称。所有这些模块都将成为您的mod-foo
模块。
要解决第二个子问题,您可以使用mod_foo.__spec__
如提到的here,mod_foo.__spec__
将是定义here 的'importlib.machinery.ModuleSpec' 的一个实例。如刚刚链接到的文档中所述,该对象将具有属性name
,即:
模块的完全限定名称的字符串。
因此我们需要检查pkg-block
是否在mod_foo.__spec__.name
为每个mod_foo
提供的完全限定名称中。
将所有这些放在一起,类似于以下代码的内容应该可以满足您的需要:
import modulefinder
def verify_no_banned_package(pkg_foo_modules, pkg_ban):
"""
Package Checker
:param pkg_foo_modules: list of the pathnames of the modules in pkg-foo
:param pkg_ban: banned package
:return: True if banned package not present in imports, False otherwise
"""
imported_modules = set()
for mod in pkg_foo_modules:
mod_finder = modulefinder.ModuleFinder()
mod_finder.run_script(mod)
mod_foo_import_mods = mod_finder.modules.keys()
imported_modules.update(mod_foo_import_mods)
for mod_foo in imported_modules:
mod_foo_parent_full_name = mod_foo.__spec__.name
if pkg_ban in mod_foo_parent_full_name.split(sep="."):
return False
return True
【讨论】:
这可能会起作用,除非我的pkg-foo
使用命名空间包,而 ModuleFinder
在 2021 年 12 月 27 日使用 Python 3.8.12 时不适用于 bugs.python.org/issue40350 命名空间包( 流泪)。另一个注意事项是,我将这个答案的pkg_foo_modules
的pkg_foo_modules
参数传递给了绝对模块路径列表(例如["/abs/path/to/file.py", ...]
)。我能够使用 setuptools.find_packages
和 pkgutil.iter_modules
per this answer 来实现这一点。以上是关于如何导入python中的模块的主要内容,如果未能解决你的问题,请参考以下文章