一、学前花絮
在 Python 开发中,“. ”这个符号在 import 语句里频繁出现,但对于初学者来说,它既神秘又容易报错。其实,“. ”的含义非常简单,但它高度依赖于代码的运行上下文。
为了彻底讲清楚这个问题,我们通过一个具体的项目示例来演示。你会发现,“. ”其实就是一个指向“当前所在目录”的指针,但它在主程序和模块中的“家”是不一样的。
二、Python包模块相对导入示例
2.1 项目架构设计
为了说明相对导入的真正含义,我们先设计一个项目目录结构:

以上目录结构是一个典型的项目架构,其中my_project可以认为是你的项目根目录(也是包模块),包含了主程序main.py与初始化文件__init__.py(这个文件也是区分包模块与单纯的目录的区别)。根目录包含了2个子目录log和core,分别是记录日志和核心业务逻辑的。在子目录下面又分别有不同的文件。
2.2 代码实现与演示
1. core/utils.py
这个文件是被导入的工具模块。我们在这里打印出它对自己位置的理解。

2. core/product.py
这个文件是核心业务模块。它尝试使用相对导入(.)来调用 utils.py。

3. main.py
这是程序的入口。它负责启动整个程序。

在pycharm中运行main程序执行如下:

在cmd下执行main程序如下:

我们观察以上的输出结果是一样的。
以上的结果分析:
通过代码输出的路径,我们可以清晰地看到 . 的行为:
1. main.py 中的 .: 输出的是 D:\my_project。说明在主程序中,. 就是项目根目录。
2. product.py 中的 .: 输出的是 D:\my_project\core。说明在 core 包内部,. 变成了 core 目录。
结论:
“.” 不是一个全局的、固定的根目录。它是一个局部坐标。每个文件都有自己的“原点”(.),它永远指向该文件所在的那个文件夹。Python 的导入系统会根据这个局部坐标,去找到同级的其他模块。
2.3 容易迷惑的错误
初学者容易犯的错误是什么呢?如果我们按照以上方法执行程序,没有问题,因为是按照包模块去进行的。而当我们单独执行product.py文件的时候:
cd core python product.py |
运行后错误如下:

那为什么呢?难道一个项目中那么多的构成文件,还不能单独执行了?必须要集成到一起才可以?
当然不是,python之所以说语法简单,它也是非常灵活的,单独的模块py文件当然可以运行,但并不能使用“.”进行相对导入!再明确一下这个“.”就是为了包模块而生的:
当你直接运行 python core/product.py 时,Python 认为 product.py 是一个独立的脚本,不属于任何包。此时,product.py 的 __package__ 是 None,Python 不知道 . 指的是哪个目录,所以找不到 utils.py。
正确的解决方案(如果你不在保内运行非要单独运行product.py)
方案 A:使用绝对导入(推荐)

方案 B:把当前目录加入搜索路径(临时调试)
在 product.py 的最开头,手动把当前目录加入 Python 的搜索路径,这样它就变成了“顶层包”:

方案 C:使用 -m 参数运行(最符合 Python 规范)
不要在 core 目录下运行,而是回到项目根目录,把 product.py 当作一个模块来运行:

这样 Python 就会把它当作 core 包的一部分来处理,. 就能正常工作了。
以上内容的总结:
● “.”是局部的: 它永远指向当前代码文件所在的文件夹。
● 包是容器:“.”只有在包(有 __init__.py 的文件夹)内部才有意义。
● 运行姿势: 如果你使用了“.”,就不要直接运行那个文件,而是通过主程序(main.py)来导入它,或者使用 python -m core.product 的方式运行。
三、小结
通过了解python的相对导入含义,我们更深入了python解释器运行的逻辑。对于实际的工程项目来说,肯定会包含比较复杂的目录或者包模块,那么如何清晰理解逻辑导入的含义是非常重要的。特别是在调试程序的时候,有的时候并不是在主程序入口调试子目录中的程序,那么就存在单独的py文件如何导入模块的问题。
让我们保持学习热情,多做练习。我们下期再见!