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

2018-06-14  本文已影响32人  Jacob_LJ

Python基础-包/模块

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所有代码

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 后面的变量名称到当前命名空间

Test2.py

name = "Test2"
age = 2

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

import Test2


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

>>>>打印结果
执行了Test2所有代码
Test2
2
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 ... 导入方式底层执行步骤区别

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 第一层级

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. 追加路径的方式(修改 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最后的,当 工程目录中有同名模块时,会优先加载工程内的同名模块

2.2 第二次导入

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

import sys
sys.modules

3 导入模块的常见场景?

3.1 局部导入

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

testFunc()

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

3.2 覆盖导入

from  package1 import sys
print(sys.name)

3.3 循环导入

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 可选导入

try:
    import Testxxx as t
except ModuleNotFoundError:
    import Test1 as t

print(t.name)

3.5 包内导入


明确一下概念

import sys
sys.path

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

import package1.Test3

Test3

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
上一篇 下一篇

猜你喜欢

热点阅读