简体   繁体   English

Python 3 - 导入系统和相对导入概念

[英]Python 3 - Importing System and Relative Imports concept

I am fairly new to Python, specifically Python3;我对 Python 相当陌生,特别是 Python3; learning the ropes here.在这里学习绳索。

Problem Summary : Can be summarized in not fully understanding how the import system works in Python and what exactly is relative import, though I'll mention what I actually understand and please correct me if there are any misconceptions here.问题总结:可以概括为没有完全理解Python中的导入系统是如何工作的,以及究竟什么是相对导入,尽管我会提到我真正理解的内容,如果这里有任何误解,请纠正我。

My Understanding : Python behaves differently whether a python file "file.py" is ran from the REPL using python file.py vs importing this file using import file.py , this behavior differs in the value assignment of __name__ variable;我的理解:无论是使用python file.py从 REPL 运行 python 文件“file.py”还是使用import file.py导入此文件,Python 的行为都不同,这种行为在__name__变量的赋值上有所不同; in the first case its given the value '__main__' and in the second case its given the value __file__ which is usually the fully qualified path followed to import this file.在第一种情况下,它给出了值'__main__' ,在第二种情况下,它给出了值__file__ ,它通常是导入此文件所遵循的完全限定路径。 Upon importing a module;导入模块时; python doesn't save the hierarchy it has followed to fetch it, hence if there are modules needed to be imported within the same level of the previously imported module, then a fully qualified name is still needed. python 不会保存它所遵循的层次结构来获取它,因此如果需要在先前导入的模块的同一级别中导入模块,那么仍然需要一个完全限定的名称。


Context:语境:

Consider the following directory考虑以下目录

Main:
|______ main.py
|
|______ PackageOne:
|
|______ ______ __init__.py
|
|______ ______ ModuleOne.py
|
|______ ______ PackageTwo:
|
|______ ______ ______ __init__.py
|
|______ ______ ______ ModuleTwo.py

structure details:结构细节:

  • main.py => program entry ("first file to be ran via IDE"). main.py => 程序入口(“通过 IDE 运行的第一个文件”)。

  • PackageOne => a package folder containing an __init__.py initializer and a ModuleOne.py module. PackageOne => 包含__init__.py初始化程序和ModuleOne.py模块的包文件夹。

    • __init__.py => the initializing python file of PackageOne which imports ModuleOne.py module and PackageTwo __init__.py => 导入ModuleOne.py模块和PackageTwoPackageOne的初始化 python 文件
    • ModuleOne.py a module python file that contains a function named FuncOne() ModuleOne.py 一个模块 python 文件,其中包含一个名为FuncOne()的函数
  • PackageTwo => a package folder containing an __init__.py initializer and a ModuleTwo.py module. PackageTwo => 包含__init__.py初始化程序和ModuleTwo.py模块的包文件夹。

    • __init__.py => the initializing python file of PackageTwo which imports ModuleTwo.py module __init__.py => 导入ModuleTwo.py模块的PackageTwo的初始化 python 文件
    • ModuleTwo.py a module python file that contains a function named FuncTwo() Files content: ModuleTwo.py一个模块 python 文件,其中包含一个名为FuncTwo()的函数文件内容:

      main.py :主.py

    import PackageOne PackageOne / __init__ :导入 PackageOne PackageOne / __init__

    print(f"From ParentPackage, Name:{ name }") import PackageTwo, ModuleOne print(f"From ParentPackage, Name:{ name }") import PackageTwo, ModuleOne

PackageOne / ModuleOne : PackageOne / ModuleOne

def FuncOne():
    print(__name__)

PackageTwo/ __init__ : PackageTwo/ __init__

print(f"From ParentPackage, Name:{__name__}")
import ModuleTwo

PackageTwo/ ModuleTwo :包二/模块二

def FuncTwo():
    print(__name__)

Detailed Problem:详细问题:

  • A): An output of A):的输出

No Module Named 'PackageTwo'没有名为“PackageTwo”的模块

pops up.弹出。 and upon removing PackageTwo from import, the same occurs for ModuleOne.从导入中删除 PackageTwo 后,ModuleOne 也会发生同样的情况。

  • B): Upon refactoring the code of files like so: B):在重构文件代码时,如下所示:

    PackageOne / __init__ : PackageOne / __init__

     print(f"From ParentPackage, Name:{__name__}") from PackageOne import PackageTwo, ModuleOne

    PackageTwo/ __init__ : PackageTwo/ __init__

     print(f"From ParentPackage, Name:{__name__}") from PackageOne.PackageTwo import ModuleTwo

Running main.py causes no problems whatsoever and prints out "From ParentPackage, Name:PackageOne" and "From ChildPackage, Name:PackageOne.PackageTwo", but trying to execute any of the __init__.py files directly outputs an error of运行 main.py 没有任何问题,并打印出“From ParentPackage,Name:PackageOne”和“From ChildPackage,Name:PackageOne.PackageTwo”,但尝试执行任何__init__.py文件直接输出错误

No Module Named:'PackageOne'没有模块名称:'PackageOne'

which seems counter-intuitive to the concept of modularity in the sense of dividing the code into small parts for reusability / readability, but not actually being able to run the code from the same scope its written in, or the flexibility of importing it from a different scope.从将代码分成小部分以实现可重用性/可读性的意义上来说,这似乎与模块化的概念背道而驰,但实际上无法在其写入的相同范围内运行代码,或者从导入它的灵活性范围不同。 Though I instantly thought maybe relative import would probably help solve this case?虽然我立刻想到也许相对导入可能有助于解决这种情况? but it seems that my understanding of relative import is not concrete, so a brush up on this topic would really help.但似乎我对相对导入的理解并不具体,所以复习一下这个话题真的很有帮助。

Thank you!谢谢!

I've found a way to some what solve this issue, but this wouldn't be considered a decent approach as it increases coupling between dependancies.我找到了解决这个问题的方法,但这不会被认为是一种体面的方法,因为它增加了依赖关系之间的耦合。

if __name__ != "__main__":
    import importlib
    my_module = importlib.import_module(f'{__name__}.ModuleOne')
else:
    import ModuleOne

importlib.import_module(f'{__name__}.ModuleOne') is a function that imports Modules and by providing the path / caller of the import keyword __name__ , I'am able to import ModuleOne from any scope. importlib.import_module(f'{__name__}.ModuleOne')是一个导入模块的函数,通过提供导入关键字__name__的路径/调用者,我可以从任何范围导入ModuleOne

this would be placed within the PackageOne \ __init__.py , by doing so I can easily run the __init__.py file directly or by importing it, but by explicitly typing a string representation of ModuleOne within the importing function it makes it impossible to auto-refactor upon changing the dependancies name.这将被放置在PackageOne \ __init__.py中,通过这样做我可以轻松地直接运行__init__.py文件或通过导入它,但是通过在导入函数中显式键入ModuleOne的字符串表示形式,它无法自动运行在更改依赖项名称时重构。 This answer is to some what guide myself in the direction of how things work and maybe find a better approach.这个答案是一些指导我了解事情如何运作的方向,也许会找到更好的方法。

I think I've finally figured it out, there were a couple of missing information, please correct me if I am wrong.我想我终于弄明白了,有一些缺失的信息,如果我错了,请纠正我。

  1. from __a__ import __b__ can be in two contexts: from __a__ import __b__可以在两种情况下:

    • Script File: __a__ here can only be the name of a module or a sub directory, while __b__ can be an attribute of the module to be inserted.脚本文件:这里的__a__只能是模块名或子目录,而__b__可以是要插入的模块的属性。 which begs the question, how to import modules from different directories if the python file is being ran as a script?这就引出了一个问题,如果 python 文件作为脚本运行,如何从不同的目录导入模块? we start adding the directory to the list of directories that python searches within using sys.path.append("directory path needed") , importing sys of course prior to that.我们开始使用sys.path.append("directory path needed")将目录添加到 python 搜索的目录列表中,当然在此之前导入sys

    • Module: module meaning that this python file is going to be imported into another module or directly into the script file that is being ran, hence the __a__ part can either be a module name from a directory that exists in the sys.path like the examples above, or a directory;模块:模块意味着这个 python 文件将被导入另一个模块或直接导入正在运行的脚本文件中,因此__a__部分可以是sys.path中存在的目录中的模块名称,如示例以上,或目录; for example from child import module .例如from child import module Note : It's important to know that when using Absolute paths its important to start the directory path from the root package up to the required package or module.注意:重要的是要知道,当使用绝对路径时,从根包开始到所需的包或模块的目录路径很重要。 Instead relative paths can be used.相反,可以使用相对路径。

    • Conclusion: __name__ when is evaluated to "__main__" due to running the python file as a script, its unable to traverse to other paths using the from keyword.结论:由于将python文件作为脚本运行, __name__当被评估为"__main__"时,它无法使用from关键字遍历其他路径。

  2. __init__.py files aren't meant to be ran as scripts hence they sometimes include importing modules or other packages using the from directory keyboard. __init__.py文件并不打算作为脚本运行,因此它们有时包括使用from directory键盘导入模块或其他包。

  3. Correlation of the above two points, the relative import such from . import package以上两点的相关性,相对导入如from . import package from . import package is only valid for modules and __init__.py of packages which is a module itself and not scripts. from . import package仅对模块和包的__init__.py有效,它们是模块本身而不是脚本。

  4. Sibling packages do not require changing directories to access each other.兄弟包不需要更改目录即可相互访问。


Summary :摘要

When working on a module you can use from directory and from . (relative paths)在处理模块时,您可以使用from directoryfrom . (relative paths) from . (relative paths) , but when working on a script if there is a specific module needed from a different directory then add it to the path using sys.path.append("directory") , finally __init__.py of packages are modules, not scripts. from . (relative paths) ,但是在处理脚本时,如果需要来自不同目录的特定模块,则使用sys.path.append("directory")将其添加到路径中,最后__init__.py包是模块,而不是脚本。 (if needed to work as a script/module then an if condition if __name__ != __main__: relative import or another work around is required). (如果需要作为脚本/模块工作,则需要 if 条件if __name__ != __main__: relative import或其他解决方法)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM