关于python:学了半天import-到底在干啥

49次阅读

共计 4154 个字符,预计需要花费 11 分钟才能阅读完成。

Python 凭什么就那么好用呢?毫无疑问,大量现成又好用的内置 / 第三方库功不可没。

那咱们是怎么应用它们的呢?

噢,对了~ 是用的 import xxx 这个语句。

之所以会有此一问,也是之前有一次应用 PyCharm 进行开发时(又)踩了个坑……

废话少说,先讲问题
像上面这样一个我的项目构造:

Projetc_example
|-- A
   |-- alpha.py
   |-- beta.py
|-- B
    |-- theta.py
|-- main
    |-- main.py

假如要在 main.py 中导入 theta.py:

# main/main.py
from B import theta

显然会导致咱们所不心愿的问题,即 Python 不晓得要到哪里去找这个名为 B 的模块(包是一种非凡的模块):

Traceback (most recent call last):
  File "main/main.py", line 1, in <module>
    from B import theta
ModuleNotFoundError: No module named 'B'

可是这就奇了怪了,为啥同样的代码,在 PyCharm 里运行就是好的了呢?

import 的查找门路
于是咱们不辞艰苦,高低求索,原来在 Python 中,import 语句实际上封装了一系列过程。

  1. 查找是否已导入同名模块
    首先,Python 会依照 import xxx 中指定的包名,到 sys.modules 中查找以后环境中是否曾经存在相应的包——不要奇怪为什么都没有导入 sys 这个模块就有 sys.modules 了。

sys 是 Python 内置模块,也就是亲儿子,导入只是意思一下,让咱们这样的外人在导入的环境中也能够应用相干接口而已,实际上相应的数据对 Python 而言从始至终都是通明的。

咱们能够导入 sys 查看一下这个对象的具体内容(节俭篇幅,做省略解决):

>>> import sys
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, ...'re': <module 're' from 'E:\\Anaconda\\Anaconda\\lib\\re.py'>, ...}

这些就都是 Python 一开始就曾经加载好的模块,也就是装置好 Python 之后,只有一运行环境中就曾经就绪的模块——只是作为外人的咱们还不能间接拿过去用,得跟 Python 报备一声:“欸,我要拿您儿子来用了嗨~”

很容易能够发现,sys.modules 中列出来的已加载模块中存在显著的不同,后面的很多模块显得很洁净,而前面的很多模块都带有 from yyy’ 的字样,并且这个 yyy 看起来还像是一个门路。

这就关系到咱们接下来要讲的步骤了。

  1. 在特定门路下查找对应模块
    后面咱们讲到了,当咱们导入某个模块时,Python 先会去查问 sys.modules,看其中是否存在同名模块,查到了那当然大快人心,Python 间接把这个模块给咱们用就好了,毕竟儿子那么多,借出去赚点外快也是好事儿不是?

可问题在于:那要是没找到呢?

这显然是一个很事实的问题。毕竟资源是无限的,Python 不可能把你可能用到的所有模块全都一股脑给加载起来,否则这样男上加男加男加男……谁也顶不住啊不是(大雾

于是乎就有人给 Python 出了个主见:那你等到要用的时候,再去找他说他是你儿子呗

Python:妙哇~

图片

有了这个思路,Python 就指定了几家特定的酒楼,说:“但凡去生产的各位,都能够给我当儿子。”

就这样,一些原本不是 Python 亲儿子的人,出于各种起因汇集到了这几家酒楼,以雇佣兵的身份随时筹备长期称为 Python 的儿子。

这可就比周文王开局就收 100 个义子优雅多了,养家糊口的压力也就没那么大了(Python:什么?我的亲儿子都不止 100 个?你说什么?听不见啊——

回到正经的画风来——

实际上,在 Python 中,sys.path 保护的就是这样一个 py 交易的后果 (诶?如同莫名发现了什么),其中保留的内容就是这几家“指定酒楼”,也就是当 Python 遇到不意识的儿子 模块时,就会去实地查找的门路。

咱们也能够打印进去看看具体内容:

>>> sys.path
['','E:\\Anaconda\\Anaconda\\python37.zip','E:\\Anaconda\\Anaconda\\DLLs','E:\\Anaconda\\Anaconda\\lib','E:\\Anaconda\\Anaconda','E:\\Anaconda\\Anaconda\\lib\\site-packages','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib','E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']

大体上就是装置环境时配置的一些包所在门路,其中第一个元素代表以后所执行脚本所在的门路。

也正是因而,咱们能够在同一个目录下,大大方方地调用其余模块。

3. 将模块与名字绑定
找到相应的非亲生模块还没完,加载了包还得为它调配一个指定的名字,咱们能力在脚本中应用这个模块。

当然少数时候咱们感知不到这个过程,因为咱们就是一个 import 走天下:

import sys
import os
import requests

这个时候咱们指定的模块名,实际上也是指定的稍后用来调用相应模块的对象名称。

换个更显著的:

import requests as req

如果这个时候只应用了第二种形式来导入 requests 这个模块,那么很显然在之后的程序流程中,咱们都不能应用 requests 这个名字来调用它而该当应用 req。

这就是 Python 导入过程中的名称绑定,实质上与失常的赋值没有太大区别,加载好了一个对象之后,而后为这个对象赋一个指定的变量名。

当然即便是曾经加载好的模块,咱们也能够利用这个名称绑定的机制为它们取别名,比方:

>>> import sys
>>> import sys as sy
>>> sys.path
['','E:\\Anaconda\\Anaconda\\python37.zip','E:\\Anaconda\\Anaconda\\DLLs','E:\\Anaconda\\Anaconda\\lib','E:\\Anaconda\\Anaconda','E:\\Anaconda\\Anaconda\\lib\\site-packages','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib','E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']
>>> sy.path
['','E:\\Anaconda\\Anaconda\\python37.zip','E:\\Anaconda\\Anaconda\\DLLs','E:\\Anaconda\\Anaconda\\lib','E:\\Anaconda\\Anaconda','E:\\Anaconda\\Anaconda\\lib\\site-packages','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32','E:\\Anaconda\\Anaconda\\lib\\site-packages\\win32\\lib','E:\\Anaconda\\Anaconda\\lib\\site-packages\\Pythonwin']
>>> sys == sy
True

问题解决
好了,下面就是对 Python 导入机制的大抵介绍,然而说了半天,咱们的问题还没有解决:在我的项目中如何简洁地跨模块导入其余模块?

在应用 PyCharm 的时候倒是所有顺遂,因为 PyCharm 会主动将我的项目的根目录退出到导入的搜寻门路,也就是说像上面这样的我的项目构造,在任意模块中都能够很天然地通过 import A 导入模块 A,用 import B 导入模块 B。

Projetc_example
|-- A
   |-- alpha.py
   |-- beta.py
|-- B
    |-- theta.py
|-- main
    |-- main.py

然而在非 IDE 环境中呢?或者说就是原生的 Python 环境中呢?

很天然地咱们就会想到:那就手动把我的项目根目录退出到 sys.path 中去嘛。说起来也跟 PyCharm 做的事没差呀

能够,贫道看你很有悟性,不如跟我去学修仙吧

所以咱们就通过 sys 和 os 两个模块七搞八搞(这两个模块以前有过介绍,不再赘述)——

噔噔噔噔——好使了

# Peoject_example/A/alpha.py
print("name:" + __name__)
print("file:" + __file__)

def al():
    print("Importing alpha succeeded.")
main.py 中则退出一个逻辑,在 sys.path 中减少一个我的项目根目录:import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

import A.alpha


A.alpha.al()

# name: A.alpha
# file: *\Project_example\A\alpha.py
# Importing alpha succeeded.

功败垂成,风紧扯呼~

总结
本文借由一个易现问题引出对 Python 导入机制的介绍,实际上限于篇幅,导入机制只是做了一个概览,具体的内容还要更加简单。本文讲到的这三步则实用于比拟常见的情景,理解了这三步也足以应酬很多问题了。

以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈,发送“J”即可收费获取,每日干货分享

正文完
 0