我对Python相当陌生,特别是Python3;在这里学习诀窍。
问题摘要:可以概括为不完全理解导入系统在Python中的工作方式以及什么是相对导入,尽管我会提到我实际理解的内容,如果这里有任何误解,请纠正我。
我的理解:Python的行为不同;文件.py";使用python file.py
从REPL运行,而使用import file.py
导入此文件,此行为与__name__
变量的值分配不同;在第一种情况下给定值CCD_ 4,在第二种情况下给出值__file__
。导入模块后;python不会保存获取它所遵循的层次结构,因此,如果在之前导入的模块的同一级别中需要导入模块,那么仍然需要一个完全限定的名称。
上下文:
考虑以下目录
Main:
|______ main.py
|
|______ PackageOne:
|
|______ ______ __init__.py
|
|______ ______ ModuleOne.py
|
|______ ______ PackageTwo:
|
|______ ______ ______ __init__.py
|
|______ ______ ______ ModuleTwo.py
结构详细信息:
main.py
=>程序条目("通过IDE运行的第一个文件")。CCD_ 7=>包含
__init__.py
初始值设定项的包文件夹以及CCD_ 9模块。__init__.py
=>导入ModuleOne.py
模块的PackageOne
的初始化python文件和PackageTwo
- ModuleOne.py是一个模块python文件,其中包含一个名为
FuncOne()
的函数
CCD_ 15=>包含
__init__.py
初始值设定项的包文件夹以及CCD_ 17模块。__init__.py
=>导入ModuleTwo.py
模块的PackageTwo
的初始化python文件ModuleTwo.py
一个模块python文件,包含一个名为FuncTwo()
的函数文件内容:
main.py:
导入PackageOnePackageOne/__init__:
print(f"来自ParentPackage,名称:{名称}")导入PackageTwo,ModuleOne
PackageOne/ModuleOne:
def FuncOne():
print(__name__)
第二包/__init__:
print(f"From ParentPackage, Name:{__name__}")
import ModuleTwo
包二/模块二:
def FuncTwo():
print(__name__)
详细问题:
- A):的输出
没有名为"PackageTwo"的模块
弹出。从导入中删除PackageTwo后,ModuleOne也会发生同样的情况。
B):在重构文件代码时,如下所示:
PackageOne/__init__:
print(f"From ParentPackage, Name:{__name__}") from PackageOne import PackageTwo, ModuleOne
第二包/__init__:
print(f"From ParentPackage, Name:{__name__}") from PackageOne.PackageTwo import ModuleTwo
运行main.py不会引起任何问题;来自ParentPackage,名称:PackageOne";以及";来自ChildPackage的名称:PackageOne.PackageTwo";,但是尝试执行CCD_ 23文件中的任何一个直接输出的错误
没有命名的模块:"PackageOne">
这似乎与模块化概念背道而驰,模块化概念将代码划分为小部分以实现可重用性/可读性,但实际上无法从编写代码的同一范围运行代码,或者从不同范围导入代码的灵活性。虽然我立刻想到也许相对重要性可能会帮助解决这个问题?但我对相对重要性的理解似乎并不具体,所以复习一下这个话题会很有帮助。
谢谢!
我已经找到了解决这个问题的方法,但这不是一个好方法,因为它增加了依赖关系之间的耦合。
if __name__ != "__main__":
import importlib
my_module = importlib.import_module(f'{__name__}.ModuleOne')
else:
import ModuleOne
importlib.import_module(f'{__name__}.ModuleOne')
是一个导入模块的函数,通过提供导入关键字__name__
的路径/调用方,我可以从任何范围导入ModuleOne
。
这将被放置在PackageOne __init__.py
中,通过这样做,我可以很容易地直接或通过导入来运行__init__.py
文件,但通过在导入函数中显式键入ModuleOne
的字符串表示,就不可能在更改依赖项名称时自动重构。这个答案是为了引导自己朝着事情如何运作的方向前进,也许还能找到更好的方法。
我想我终于明白了,有几个信息缺失,如果我错了,请纠正我。
-
从
__a__
导入__b__
可以在两个上下文中:-
脚本文件:这里的
__a__
只能是模块或子目录的名称,而__b__
可以是要插入的模块的属性。这就引出了一个问题,如果python文件作为脚本运行,如何从不同的目录导入模块?我们开始使用sys.path.append("directory path needed")
将目录添加到python搜索的目录列表中,当然在此之前导入sys
。 -
Module:Module意味着这个python文件将被导入另一个模块或直接导入正在运行的脚本文件,因此
__a__
部分可以是sys.path
中存在的目录中的模块名称,也可以是目录;例如CCD_ 38注意:重要的是要知道,当使用绝对路径时,启动从根包到所需包或模块的目录路径非常重要。相反,可以使用相对路径。 -
结论:由于将python文件作为脚本运行,
__name__
在评估为'__main__'
0时,无法使用from
关键字遍历到其他路径。
-
-
__init__.py
文件并不意味着要作为脚本运行,因此它们有时包括使用from directory
键盘导入模块或其他包。 -
以上两点的相关性,
from . import package
这样的相对导入只对模块和包的__init__.py
有效,包本身是模块而不是脚本。 -
兄弟包不需要更改目录即可相互访问。
摘要:
在处理模块时,可以使用from directory
和from . (relative paths)
,但在处理脚本时,如果需要来自不同目录的特定模块,则使用sys.path.append("directory")
将其添加到路径中,最后包的__init__.py
是模块,而不是脚本。(如果需要作为脚本/模块工作,则需要if条件if __name__ != __main__: relative import
或其他变通方法)。