• 相对导入
  • 总结
  • 如果你曾写过包含有一个文件以上的项目,你可能会使用过 import 语句。对于写过多个项目的Python老鸟来说,
    导入 import
    也会让他们费解。而你正在阅读这篇教程的原因有可能是因为你想要更深入的了解Python的导入 ,特别是在绝对和相对导入方面。

    通过阅读本教程,你将能了解到这两种方式的不同之处,以及其优缺点。让我们开始吧!

    你需要对Python的,
    模块 module

    package
    有一个良好的理解才能更好的明白导入的工作原理。一个Python模块实际上就是一个文件,它的文件格式为 .py 。一个Python包就是一个
    文件夹 folder
    ,里面含有模块文件(在Python 2中,这个文件夹要有一个 __init__.py 文件)。

    当你在一个模块中写的代码需要使用到其他模块或者包的代码要怎么办?导入它!

    但是这个导入到底是如何工作的呢?假如你导入了一个模块,代码如下:

    import abc
    

    Python首先要做的就是在sys.modules中查找这个abc名称。它是先前曾被导入过的所有模块的缓存。简单来说,就是之前被导入过的模块,都被暂时保存在里面。

    如果在这个缓存中找不到这个名称,Python接下来会去
    内置的模块 built-in modules
    中查找。这些内置模块是Python预装的,可以在Python标准库中查看详情。假如还是没能在内置模块中找到它,Python将会去sys.path 定义的文件夹列表中搜索。通常来说,这个列表会包括当前目录,并且会先在里面搜索。

    当Python找到了模块,它会将这个模块绑定到局部作用域。这就表示当前这个 abc已经被定义了并且在被当前文件使用而不会抛NameError的异常。

    如果这个模块名没有被找到,有就会抛出ModuleNotFoundError这个异常。你可以在Python的官方文档找到更多内容。

    注意 :安全问题

    要注意当前的导入系统有一些重大的安全风险。这是大部分是由于Python本身的灵活性。例如缓存的模块是可写并且有可能可以通过导入系统重写Python的核心函数。从第三方包中导入也会让你的应用暴露于安全隐患中。

    以下是一些有用的资源关于这些安全隐患,并且关于如何解决:

    • 10个常见的Python陷阱以及如何避免(https://hackernoon.com/10-common-security-gotchas-in-python-and-how-to-avoid-them-e19fbe265e03)
    • #168节:10个Python安全漏洞以及如何修补(https://talkpython.fm/episodes/show/168/10-python-security-holes-and-how-to-plug-them)

    现在你已经了解导入的工作原理了,让我们来看一下它的语法吧。你可以导入包和模块。(要注意一下,导入一个包的时候,实际上就是导入包里面的__init__.py作为模块)你也可以从一个包或者模块中导入指定的对象。

    一般有两种导入的方法。当你使用第一个,你可以直接导入该资源,如下:

    import abc
    

    abc可以是一个包或者模块

    当你使用第二种语法,你从其他包或者模块中导入资源。看下面这个例子:

    from abc import xyz
    

    xyz可以是一个模块、
    子包 subpackage

    对象 object
    ,例如
    class
    或者
    函数 function

    你也可以选择重命名导入的资源,如下:

    import abc as other_name
    

    这会在脚本中重命名这个已经导入的模块abcother_name。现在必须使用other_name进行引用,不然就不被识别。

    PEP8 是Python的官方编码规范,里面有几个点也是适用于导入规范,以下是总结:

    1. 导入应该总是写在最前面,但需尾随任何模块单行注释和文档注释。
    2. 导入应该根据导入的内容进行分类,一般有三种:
      • 导入标准库(Python的内置模块)
      • 导入相关的第三方包(不属于当前应用的第三方包)
      • 导入本地应用的模块(属于当前应用的模块)
    3. 每一组的导入都应该用空白行分隔

    请看下面这个例子:

    """展示一个标准的导入规范
    注意,导入语句应该位于文档注释之后
    # 导入标准库
    import datetime
    import os
    # 导入第三方库
    from flask import Flask
    from flask_restful import Api
    from flask_sqlalchemy import SQLAlchemy
    # 导入本地模块
    from local_module import local_class
    from local_package import local_function
    

    以上的导入语句被分成了三个部分,通过空白行分隔。并在每一个部分中,是根据字母排序的。

    你已经了解到了如何写导入语句并且像一个专家一样知道如何写规范的导入语句。现在是时候学习关于绝对引入的内容了。

    绝对导入通过使用被导入资源在项目根目录的完整路径进行导入。

    语法与实例

    假如你有以下目录结构:

    └── project
        ├── package1
        │   ├── module1.py
        │   └── module2.py
        └── package2
            ├── __init__.py
            ├── module3.py
            ├── module4.py
            └── subpackage1
                └── module5.py
    

    这里有个目录project,它包含了两个子目录:package1package2。其中package1有两个文件:module1.pymodule2.py

    package2目录有三个文件:两个模块, module3.pymodule4.py,也有一个初始化文件 __init__.py。并且也包括一个目录,subpackage,它包含一个文件,module5.py

    我们假设以下内容:

    1. package1/module2.py有一个函数,叫function1
    2. package2/__init.py有一个类,叫class1
    3. package2/subpackage1/module5.py有一个函数,叫function2

    以下是绝对导入的例子:

    from package1 import module1
    from package1.module2 import function1
    from package2 import class1
    from package2.subpackage1.module5 import function2
    

    要注意你必须在
    顶级包目录 top-level package
    下提供每个包或者文件具体的路径。某种程度上和其文件路径相似,但是我们会使用
    dot
    (.),而不用
    斜线 slash
    (/)。

    应该优先考虑使用绝对路径,因为其更简单明了。使用它后,仅通过导入语句,就知道资源是从哪里导入的。而且,即使当前位置的导入语句改变了,绝对导入还是会保持有效。并且事实上,PEP8 推荐使用绝对导入。

    当然,有时候绝对路径会变得冗长,取决于目录结构的复杂程度。想象一下以下这个语句:

    from package1.subpackage2.subpackage3.subpackage4.module5 import function6
    

    这是不是很可笑?幸运的是,相对导入在这种情况下是一个绝佳的选择!

    相对导入指定了被导入资源是相对于当前的位置 - 也就是,这个位置就是导入语句所在的地方。有两种类型的相对导入:隐式和显式。隐式相对导入已经在Python3中被弃用了,所以这里我就不多讲了。

    语法和实例

    相对导入的语法取决于当前的位置和被导入模块、包以及对象的位置。以下是一些例子:

    from .some_module import some_class
    from ..some_package import some_function
    from . import some_class
    

    你能看到至少有一个点在每一个导入语句的前面。相对导入利用点符号来指定位置。

    单个点表示模块或者包的引用是在同一个位置的同一个目录下。两个点表示它是在当前位置的父目录中 - 意思是指上一级目录。三个点表示它位于祖父母目录中,以此类推。如果你使用类Unix系统的话,一定有熟悉的感觉!

    假设你有和之前一样的目录结构:

    └── project
        ├── package1
        │   ├── module1.py
        │   └── module2.py
        └── package2
            ├── __init__.py
            ├── module3.py
            ├── module4.py
            └── subpackage1
                └── module5.py
    

    这里有个目录project,它包含了两个子目录:package1package2。其中package1有两个文件:module1.pymodule2.py

    package2目录有三个文件:两个模块, module3.pymodule4.py,也有一个初始化文件 __init__.py。并且也包括一个目录,subpackage,它包含一个文件,module5.py

    重温以下内容:

    1. package1/module2.py有一个函数,叫function1
    2. package2/__init.py有一个类,叫class1
    3. package2/subpackage1/module5.py有一个函数,叫function2

    你可以通过以下方法在package1/module1.py文件中导入导入function1

    # package1/module1.py
    from .module2 import function1
    

    你在这里只需要使用一个点,因为module2.py和当前的模块module1.py是在同一个路径下面。

    你也可以在package2/module3.py中导入class1function2通过以下方法:

    # package2/module3.py
    from . import class1
    from .subpackage1.module5 import function2
    

    在第一个导入语句中,单个点代表你从当前包中导入class1。要谨记,导入一个包实际上是导入包里的__init__.py文件作为模块。

    第二个导入语句中,你再次使用了单个点,这是因为subpackage1和当前模块module3.py是在同一个目录中。

    相对导入最明显的优点就是非常简洁。取决于当前的位置,它可以将之前那可笑的冗长语句缩减到以下:

    from ..subpackage4.module5 import function6
    

    不幸的是,相对导入可能会引起混乱,特别是一些目录结构可能会改变的共享项目。且相对导入不像绝对导入那样可读,不能轻易的从导入语句中查看资源被导入的位置。

    好样的!你学完了这个关于绝对导入和相对导入
    速成课 crash course
    。你现在已经掌握了导入的工作原理。并且你也学到了编写导入语句的最佳实践,以及了解了绝对导入和相对导入的异同之处。

    现在你可以用你刚刚学到的新技能自信满满的在Python中导入标准库、第三方包和你自己的本地包了。要记住,一般情况下,你应该选择使用绝对导入而不使用相对导入,除非路径非常的复杂,不然会使导入语句冗长。

    Hi there, I’m Mbithe! I’m an experienced software engineer with interests in artificial intelligence and machine learning. I’m currently a postgraduate student at The University of Manchester, studying Advanced Computer Science with a specialisation in AI.

    大家好,我是Mbithe! 我是一个资深的软件开发工程师,我喜欢人工智能和机器学习。我目前在
    曼彻斯特大学 The University of Manchester
    读研,学习
    高级计算机科学 Advanced Computer Science
    ,主攻人工智能。

            python 中的包和模块,首先是按照代码的功能进行整理整合,想相似功能的代码/大量代 码整理到一起方便统一管理        模块(module):python 中每个 python 文件就是一个模块,每个 python 文件中,封装... PEP 328 – Imports: Multi-Line and Absolute/RelativePEP 328 – 多行的import和绝对/相对引用 通过后面文档的翻译,先简单总结一下结论: 一、关于from … import …写得太长的问题: from xxx import aaa,bbb,ccc…如果后面比较长的话,可以使用: from xxx import (aaa,bbb,ccc, 实际项目中遇到python模块相互引用问题,查资料,终于算是弄明白了。 首先交叉引用或是相互引用,实际上就是导入循环,关于导入循环的详细说明,可见我摘自《python核心编程》第二版的摘抄:Python导入循环方法。 附录给了一种解决交叉引用的方法,试了,不行,但关于交叉引用问题本身说明的很清楚,如果不清楚什么是交叉引用,可看附录一。 循环引用python圈关注的并不多,语言上没有提供防止循环依赖的机制。 总的来说,应该在总体结构上避免模块之间互相依赖,即:A依赖B,B就不要依赖A,这也是代码重构的一个目标。 对于紧急情况,往往来不及对代码大动。 只要找到导致循环引用的模块(最少两个),把引 绝对引用python a/aa.py的时候到底发生了什么 这个时候sys.path.append会将执行文件所在的目录即 a/ 放在sys.path中。后面所有文件的import都会在这个目录下找。如果aa是这样的。 from bb import i 那么很明显可以找到在a/目录下找到bb 但是假如你运行的是python main.py import a.aa 那么现在加入到sys.path的就 当前工作目录: 所有没有从根文件夹开始的文件名或路径,都可以认定为当前工作目录。 在 Python 中,利用 os.getcwd() 函数可以取得当前工作路径的字符串,还可以利用 os.chdir() 改变它。例如 import os print(os.getcwd()) os.chdir('F:\python_project') print(os.getcwd()) 绝对路径与相对路径 绝对路径:总是从根文件夹开始,Window 中以盘符(C:、D:)作为根文件夹 相对路径:指文件相对于当前工作目录所在的位置。例如,当前工作目录为 C:\Windows\System32,若 dem Python 是一门优美简单、功能强大的动态语言。在刚刚接触这门语言时,我们会被其优美的格式、简洁的语法和无穷无尽的类库所震撼。在真正的将python应用到实际的项目中,你会遇到一些无法避免的问题。最让人困惑不解的问题有二类,一 单元格的相对引用是基于包含公式和引用的单元格的相对位置而言的。如果公式所在的单元格的位置改变,引用也将随之改变,如果多行或多列地复制公式,引用会自动调整。默认情况下,新公式使用相对引用; 单元格的绝对引用则总是在指定位置引用单元格(如$F$3)。如果公式所在单元格的位置改变,绝对引用的单元格也始终保持不变,如果多行或多列地复制公式,绝对引用将不作调整。相对引用绝对引用; 单元格的相对引用是基于包... http://kuanghy.github.io/2016/07/21/python-import-relative-and-absolute Python 相对导入与绝对导入,这两个概念是相对于包内导入而言的。包内导入即是包内的模块导入包内部的模块。 Python import 的搜索路径 在当前目录下搜索该模块 在环境变量 PYTHONPATH 中指定的路径列表中依次搜索...