Python基础38-包/模块(导入及其底层逻辑)

1 导入模块后具体做了什么?
2 从哪个位置找到需要导入的模块
3 导入模块的常见场景?

1 导入模块后具体做了什么?

1.1 第一次导入

  1. 在自己当下的命名空间中,执行所有代码
  2. 创建一个模块对象,并将模块内所有顶级变量以属性的形式绑定在模块对象上
  3. 在 import 的位置,引入 import 后面的变量名称到当前命名空间

1.1.1 在自己当下的命名空间中,执行所有代码

Test2.py

name = "Test2"
age = 2

print("执行了Test2所有代码")
Test1.py

import Test2


# 让后 run 该分代码
>>>>打印结果
执行了Test2所有代码
  • 也就是说 Test1.py 中导入了 Test2模块后,在运行 Test1模块代码时,会先让 Test2中的代码在 Test2 的自己的命名空间中执行 Test2的所有代码
  • 其实,从使用角度来理解的话,当然是 Test2 内的代码先跑完,Test1 中才能使用 Test2 中的变量等资源啊。

1.1.2 创建一个模块对象,并将模块内所有顶级变量以属性的形式绑定在模块对象上

(具有内存地址及类型)

Test2.py

name = "Test2"
age = 2

print("执行了Test2所有代码")
Test1.py

import Test2

# 查看Test2
print(Test2)

# 查看内存地址
print(id(Test2))

# 查看类型
print(type(Test2))

# 通过打印 Test2 对象的属性,验证在 Test1 中需要通过 模块名.属性形式访问的原因
print(Test2.__dict__)


>>>>打印结果
执行了Test2所有代码
<module 'Test2' from '/Users/xxx/Desktop/PythonProject/PackageAndModule/Test2.py'>
4560942648
<class 'module'>
# __dict__ 字典打印结果没有天上自己试验

1.1.3 在 import 的位置,引入 import 后面的变量名称到当前命名空间

  • 在 Test1 中通过 Test2变量名来使用 Test2 模块的资源
Test2.py

name = "Test2"
age = 2

print("执行了Test2所有代码")
Test1.py

import Test2


print(Test2.name)
print(Test2.age)

>>>>打印结果
执行了Test2所有代码
Test2
2
  • 将 Test2 这个指向 Test2 模块的对象变量名引入 当前 命名空间中(注意命名空间即作用域的区别)
Test1.py


def testFunc():
    import Test2 # 导入到了函数的命名空间中
    print(Test2.name)

testFunc()

print(Test2.name) # 全局命名空间不能使用 Test2

>>>>打印结果
Traceback (most recent call last):
执行了Test2所有代码
  File "/Users/xxx/Desktop/PythonProject/PackageAndModule/Test1.py", line 14, in <module>
Test2
    print(Test2.name)
NameError: name 'Test2' is not defined

1.2 第二次导入

1.2.1直接执行1.1.3这个步骤

Test2.py

name = "Test2"
age = 2

print("执行了Test2所有代码")
Test1.py

import Test2


print(Test2.name)
print(Test2.age)

# 再次导入, 打印中并没有出现两次“执行了Test2所有代码”
import Test2

>>>>打印结果
执行了Test2所有代码
Test2
2

1.3 注意

from ... import ... 导入方式与 import ... 导入方式底层执行步骤区别

  • 两种导入方式均都会大致的执行上述的3个步骤
  • 多次导入模块,该模块并不会执行多次
  • 两种导入方式并不存在哪一种更节省内存之说,区别在于把哪一部分内容拿到当前位置来用,因为一旦导入都将模块内的对象创建好。
Test2.py

name = "Test2"
age = 2

print("执行了Test2所有代码")
Test1.py

from Test2 import name

print(name)

>>>>打印结果
执行了Test2所有代码
Test2

2 模块检索顺序(从哪个位置找到需要导入的模块)

2.1 第一次导入

  • 按照模块检索路径顺序查找

1 内建模块
2 sys.path
2.1 当前目录环境变量PYTHONPATH
2.2 Python 安装路径
2.3 安装路径下的.pth 路径
2.4 lib 库 site-packages
2.5 lib 库 site-packages下的 pth 文件配置
3 其他

2.2.1 第一层级

  • 内置模块<built - in>

2.2.2 第二层级

  1. sys.path
    >>> import sys
    >>> print(sys.path)
    ['', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages']
    >>>
    
  • 构成 (如下索引并非指优先顺序)
    1.1 当前目录
    1.2 环境变量 PYTHONPATH 中指定的路径列表
    1.3 特定路径下的 .path 文件中的文件路径列表
    1.4 在 Python 安装路径的 lib 库中搜索
  1. 追加路径的方式(修改 sys.path 方式)
  • 直接修改 sys.path,只作于本次的执行代码模块
Test1.py


import sys
sys.path.append("/Users/xxx/Desktop/myTest") 
print(sys.path)

import fkm

# print(fkm.__dict__)
print(fkm.age)

>>>>打印结果
打印了abc 所有代码
12

注意该路径是追加到sys.path最后的,当 工程目录中有同名模块时,会优先加载工程内的同名模块

  • 修改环境变量 PYTHONPATH(自行百度)
    实际,修改PYTHONPATH环境变量也是修改 sys.path 列表内容,将环境变量PYTHONPATH代表的路径添加到 sys.path中,只不过是默认就给添加了,不同于通过代码修改。
    注意,系统修改 PYTHONPATH 和 pycharm 添加环境变量的区别

  • 添加.pth 文件
    添加 .pth 文件位置可通过 site 模块查看

    import site
    print(site.getsitepackages())
    
    >>>>打印结果
    ['/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Library/Python/3.6/site-packages']
    

    只需在以上目录下创建.pth 文件,然后在文件中添加对应模块路径即可

2.2 第二次导入

1 从已经加载过的模块中去找

  • 查看已加载模块
import sys
sys.modules

3 导入模块的常见场景?

3.1 局部导入

def testFunc():
    import Test2 # 导入到了函数的命名空间中
    print(Test2.name)

testFunc()

使用的时候才进行导入,此时 Test2 是一个局部变量,只存在于函数作用域中;减少内存的开销;

3.2 覆盖导入

  • 场景一:
    自定义模块和非内置的标准模块重名,根据前者的存储位置,有可能前者会覆盖后者
    建议:自定义模块命名不要与后者重名

  • 场景二:
    自定义模块与内置模块重名,内置肯定覆盖自定义
    建议:如果确实需要使用重名的自定义模块,可以使用 from ... import ...方式指明绝对路径进行导入使用

from  package1 import sys
print(sys.name)

3.3 循环导入

  • 模块 A 中导入模块 B, 模块 B 中又导入模块 A;
Test2.py


t2_1 = "t2_1"
t2_2 = "t2_2"

import Test1

print(Test1.t1_1)
print(Test1.t1_2)
Test1.py

t1_1 = "t1_1"
t1_2 = "t1_2"

import Test2

print(Test2.t2_1)
print(Test2.t2_2)

>>>>打印结果
t2_1
t2_2
t1_1
t1_2
t2_1
t2_2
分析逻辑

3.4 可选导入

  • 两个功能相近的包,根据需求优先选择其中一个导入
  • 想优先使用 A;当没有 A 的情况下,使用 B 作备份
try:
    import Testxxx as t
except ModuleNotFoundError:
    import Test1 as t

print(t.name)

3.5 包内导入


明确一下概念

  • 绝对导入和相对导入,这两个概念是相对于包内导入而言的

  • 包内导入:即是包内的模块导入包内的模块,即package1包内的 Test3 内导入 Test3_1

  • 包外导入:即是包外的模块导入包内的模块,即 Test1 内导入package1包内的 Test3

  • 查看当前文件参照的执行路径

import sys
sys.path
  • 查看当前模块名称

如果文件是直接运行的话,__name__的值是 __main__
如果是以模块的形式进行加载文件的话,该名称的值则是由加载路径决定,格式为:包名.子包名.模块名 (此时,第一层包名称作为顶级名称)
print(__name__)
Test1.py

import package1.Test3

Test3
  • 查找文件顺序都是先 内置<built-in> 再 sys.path
  • 当点击运行(使用解析器去执行)某一 py 文件时,此时程序会确定当前py 文件所在的目录,并且将该目录添加到 sys.path 中,此时的 sys.path内容基本确定

3.5.1 概念

3.5.1.1 绝对

  1. 参照sys.path路径进行检索
  2. 指明包名或模块名import afrom a import b
  3. 上述两点是基于 Python3.x 之后

3.5.1.2 相对

  1. 使用.来指代相对路径
    .:根据模块名(__name__)称所获取的当前目录
    ..:根据模块名(__name__)称所获取的上层目录
  2. 如:from . import afrom .. import a

3.5.2 补充

  1. Python3.x 版本之前,直接使用 import a。会优先从本地当前目录查找,并非是 sys.path
  2. 解决方案:from __future__ import absolute_import,使用该代码语句改变 import 行为

3.5.3 结论

  • 包内导入,使用相对导入
  • 包外导入,使用绝对导入

例子1:Test1 导入 Test3,Test3 内又导入Test3_1;即包外导入+包内导入

Test1.py

import package1.Test3
或
from package1 import Test3
Test3.py

from . import Test3_1
不建议写成   from package1 import Test3_1  ,因为当包名变化时,使用包名的地方都要改动

例子2:多层级导入


Test1.py

print("Test1")
print(__name__)
from package1 import Test3
Test2.py

print("Test2")
print(__name__)

Test3.py

print("Test3")
print(__name__)
from .package_sub1 import Test4
Test3_1.py

print("Test3_1")
print(__name__)
Test4.py

print("Test4")
print(__name__)

from .package_subsub1 import Test5
Test4_1.py

print("Test4_1")
print(__name__)
Test5.py

print("Test5")
print(__name__)

from .. import Test4_1
from ... import Test3_1
# from .... import Test2 #包内不能获取包外的文件

运行 Test1.py

>>>>打印结果
Test1
__main__
Test3
package1.Test3
Test4
package1.package_sub1.Test4
Test5
package1.package_sub1.package_subsub1.Test5
Test4_1
package1.package_sub1.Test4_1
Test3_1
package1.Test3_1
  • 可以通过上述例子,了解点点的作用
  • 注意:包内不能获取包外的文件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,922评论 17 410
  • 模块 一个模块就是包含了python定义和声明的文件,文件名就是模块名字加上.py后缀,通过导入模块来引入其他文件...
    断尾壁虎V阅读 1,595评论 0 1
  • 用 python 解释器来编程从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。 为此...
    chen_000阅读 520评论 0 3
  • 很多人都渴望成功,可能有些人却不知道怎样成功,成功要什么。所以有些人的成功就变成了空想。而我觉得,成功需要是不...
    唯美_4b88阅读 311评论 0 1
  • 1. 增加snmp v3用户 # service snmpd stop # net-snmp-config --c...
    SkTj阅读 1,140评论 0 0