惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
J
Java Code Geeks
有赞技术团队
有赞技术团队
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
G
Google Developers Blog
月光博客
月光博客
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
SegmentFault 最新的问题
博客园 - 三生石上(FineUI控件)
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
腾讯CDC
V
Visual Studio Blog
博客园 - 【当耐特】
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Engineering at Meta
Engineering at Meta
L
LangChain Blog

OhYee 博客

小鹏辅助驾驶测评|OhYee 博客 小鹏非支持手机开启自动解锁|OhYee 博客 使用函数计算实现 301 重定向|OhYee 博客 针对 HTML 内容使用 Ant Design 图片弹框|OhYee 博客 博客进程泄露及僵尸进程解决|OhYee 博客 蓝易云服务器体验|OhYee 博客 SSH 调起本地 VSCode|OhYee 博客 【2022 秋招内推】阿里云后端研发工程师|OhYee 博客 使用函数计算获取 IP 地址信息|OhYee 博客 正确获取客户端 IP/HTTP Header 也可能重复|OhYee 博客 评测 Oculus Quest2 及 BigScreen|OhYee 博客 NextJS 热重载保留状态|OhYee 博客 如何优雅地贴 gist 代码|OhYee 博客 Linux 精细化文件权限|OhYee 博客 VSCode 容器开发环境|OhYee 博客 Clash 的不兼容更新排查|OhYee 博客 Zeek 导出 PCAP|OhYee 博客 记一次 ssh 配置问题|OhYee 博客 Git Commit 规范化工具|OhYee 博客 谈谈《星之卡比-探索发现》|OhYee 博客 VSCode 快捷键绑定 Shell 命令|OhYee 博客 ASN.1 语法及 X.509 证书格式解析解析|OhYee 博客 腾讯企业邮箱忽略 MX 记录发信|OhYee 博客 Chrome/Edge 标签组插件|OhYee 博客 【应届内推】阿里云后端研发工程师|OhYee 博客 损坏的 Typecho 备份处理为 JSON|OhYee 博客 VS Code VIM 插件高效使用|OhYee 博客 SSH 正反向代理|OhYee 博客 Let's Encrypt 根证书过期引发的问题|OhYee 博客 OpenWRT 忽略内核依赖|OhYee 博客
Python引入原理|OhYee 博客
2019-01-13 · via OhYee 博客

Python引入原理分析

Python中import引入文件分为绝对引入和相对引入。

绝对引入

搜索PYTHONPATH环境变量目录里的所有文件,如果存在就引入,不存在就报错

这么就有一个问题,我们经常会有这种需求: 引入一个同级目录的文件
当不在一个package中时,通常我们会使用绝对引入来引入这个包,如:

src
├   a.py
└   b.py

如果想要在a.py中引入b.py,通常会使用import bfrom b import x
为什么这里绝对引入可以使用呢?
打印出sys.path后可以发现,当我们使用python a.py时,程序会自动将a.py所在的目录加入到PYTHONPATH

这样绝对引入时,会在当前的代码目录找到b.py并成功引入

相对引入

相对引入只能使用在package
相对于绝对引入必须要写明路径,相对引入可以使用...来引入同级或上级目录的内容,相对来说非常方便
当由于某些需要改动了模块间的关系时,改动也比较小

我们可以建立一个如下的文件夹来加深理解
根目录: root.py, root2.py, pack/

root.py

import site
import sys

print("="*10)
print("这是root文件")
print("运行参数", *sys.argv)
print("__name__\t", __name__)
print("__package__\t", __package__)
print("__file__\t", __file__)
for path in sys.path:
    print(path)
print("="*10)

try:
    from root2 import p1
    p1()
except Exception as e:
    print("Error", *e.args)

try:
    from pack.fun import p2, p3
    p2()
except Exception as e:
    print("Error", *e.args)

root2.py

import sys
print("="*10)
print("这是root2文件")
print("运行参数", *sys.argv)
print("__name__\t", __name__)
print("__package__\t", __package__)
print("__file__\t", __file__)
print("="*10)


def p1():
    print("p1")

pack/目录: __init__.py, fun.py, fun2.py

__init__.py

import sys
print("="*10)
print("这是包的__init__文件")
print("运行参数", *sys.argv)
print("__name__\t", __name__)
print("__package__\t", __package__)
print("__file__\t", __file__)
print("="*10)

fun.py

import sys

from .fun2 import p3

print("="*10)
print("这是fun文件")
print("运行参数", *sys.argv)
print("__name__\t", __name__)
print("__package__\t", __package__)
print("__file__\t", __file__)
print("="*10)


def p2():
    print("p2")


p3()

fun2.py

import sys
print("="*10)
print("这是fun2文件")
print("运行参数", *sys.argv)
print("__name__\t", __name__)
print("__package__\t", __package__)
print("__file__\t", __file__)
print("="*10)


def p3():
    print("p3")

输出结果:

==========
这是root文件
运行参数 e:\pytest\src\root.py
__name__         __main__
__package__      None
__file__         e:\pytest\src\root.py
e:\pytest\src
C:\Users\kongchenhao\AppData\Local\Programs\Python\Python37-32\python37.zip
C:\Users\kongchenhao\AppData\Local\Programs\Python\Python37-32\DLLs
C:\Users\kongchenhao\AppData\Local\Programs\Python\Python37-32\lib
C:\Users\kongchenhao\AppData\Local\Programs\Python\Python37-32
C:\Users\kongchenhao\AppData\Local\Programs\Python\Python37-32\lib\site-packages
==========
==========
这是root2文件
运行参数 e:\pytest\src\root.py
__name__         root2
__package__
__file__         e:\pytest\src\root2.py
==========
p1
==========
这是包的__init__文件
运行参数 e:\pytest\src\root.py
__name__         pack
__package__      pack
__file__         e:\pytest\src\pack\__init__.py
==========
==========
这是fun2文件
运行参数 e:\pytest\src\root.py
__name__         pack.fun2
__package__      pack
__file__         e:\pytest\src\pack\fun2.py
==========
==========
这是fun文件
运行参数 e:\pytest\src\root.py
__name__         pack.fun
__package__      pack
__file__         e:\pytest\src\pack\fun.py
==========
p3
p2

可以看出,这样写是可以正常运行的,这时候,如果我们直接执行fun.py,会报错:
ModuleNotFoundError: No module named '__main__.fun2'; '__main__' is not a package

这个很容易就可以看出来,相对引入的相对是对于__main__这个常量的
当我们使用引入一个包后,__main__的值就是当前包相对于运行的主程序的路径,自然后面跟上.xxx就变成了绝对引入

因此,虽然相对引入比绝对引入更早地加入到Python中,但是相对引入更类似于绝对引入的一个语法糖。

因此,实际上是不能够只执行一个项目中某个Package中的一个文件进行临时性的测试的(如果该文件有引入上层目录或同级目录下的内容)

但是可以通过变通的方式解决该问题:

  1. 对于有引入上级目录的内容,只需要将原来的函数入口的路径加入到PYTHONPATH中即可。通常来说,尽管可以使用sys.path加入,但是为了可移植性,应该使用命令行来动态加入。
    如powershell中可使用: $env:pythonpath=$env:pythonpath+';'+$pwd;python xx.py
    对于VSCode,可以直接使用"terminal.integrated.env.windows": { "PYTHONPATH": "${workspaceFolder}" }
  2. 对于有同级文件的引入,由于在引入包中,__name__是包路径,但是直接运行文件时,__name____main__。因此一方面可以使用绝对引入来引入同级包(但是这样也会造成移植性降低);同时也可以判断__name__的值来实现该方法。当__name____main__时使用相对引入;否则使用绝对引入。

这样就实现了在Python中不影响整个项目运行的情况下,执行一个包中的一个文件进行测试等操作。