python import

阿里云2000元红包!本站用户参与享受九折优惠!

一、命名空间

1.分类:

(1)局部命名空间:每个函数都有自已的局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。

(2)全局命名空间:每个模块都有自已的全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

(3)内置命名空间:任何模块均可访问它,它存放着内置的函数和异常。

 

2.查找顺序:局部命名空间(嵌套函数先到自己的命名空间搜索,再到父函数的命名空间搜索) ==> 全局命名空间 ==> 内置命名空间,如果均没有查找到,将引发一个 NameError 异常。

 

3.生命周期:局部命名空间在函数被调用时创建,当函数返回或抛出异常时被删除。全局命名空间在模块定义被读入时创建,一直保存到解释器退出。内置命名空间在 Python 解释器启动时创建,一直保留。

 

4.局部命名空间:

可以通过内置函数 locals() 查看。locals() 返回局部命名空间的一个拷贝,修改它对局部命名空间没有影响。


#代码
def foo():
    a = 1
    print("locals: %s" % locals())
foo()
#结果
locals: {'a': 1}


 

5.全局命名空间:

可以通过内置函数 globals() 查看。globals() 返回真实的全局名字空间,修改它会影响全局命名空间。


#代码
import copy
from copy import deepcopy
a = 1
def foo():
    b = 2
    print("globals: %s" % locals())
foo()
#结果
globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'copy': <module 'copy' from 'D:\\Python33\\lib\\copy.py'>, 'deepcopy': <function deepcopy at 0x01DB28A0>, 'a': 1, 'foo': <function foo at 0x00000208FF69C1E0>}


import module:模块自身被导入,保持自已的命名空间,访问时需要用 module.function ;

from module import function:从另一个模块中将指定的函数和属性导入到自己的命名空间,访问时可以直接用 function 。

 

6.内置命名空间

内置命名实际上存在于一个叫 __builtins__ 的模块中,可以通过 globals()[‘__builtins__’].__dict__ 查看其中的内置函数和内置异常。


#代码
print("builtins: %s" % globals()['__builtins__'].__dict__)
#结果
builtins: {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>,...


 

二、导入方式

python 导入部分的代码在 Lib/importlib 包中,官方文档

python 的导入依靠 __import__() 函数实现,其他导入方式中 import 语句是 __import__() 函数的语法糖,import_module() 函数是一个对 __import__() 进行简化的包装器。

三者区别如下:

1.import:先带有适当的参数调用 __import__() 函数,然后将返回值绑定到当前作用域中的命名空间里。

2.__import__():执行模块搜索以及在找到时的模块创建操作,返回最高层级的包或模块(如 pkg )。

3.import_module():对 __import__() 进行简化的包装器,返回指定的包或模块(如 pkg.mod )。

 

三、导入机制

1.模块与包

python 导入的都是模块对象类型( module ),模块有一种特殊类型包( package ), 任何具有 __path__ 属性的模块都会被当作是包。包有两种类型,常规包和命名空间包。

常规包通常以一个包含 __init__.py 文件的目录形式实现。(不带__init__.py 文件的目录不会被python识别为包)

第一次导入包时会执行一遍 __init__.py 文件,第一次导入模块时会执行一遍模块文件。

 

 

2.搜索顺序

首次导入一个模块时,Python 会按顺序搜索该模块,如果找到就创建一个 module 对象并初始化它; 如果未找到则会引发 ModuleNotFoundError 。

搜索的顺序是: sys.modules ==> sys.path[0] (当前目录) ==> sys.path[1:] 

 

sys.modules:执行 py 文件时,首先会默认加载一些模块( sys.builtin_module_names 列出的内置模块,标准库)并存放在 sys.modules 中;之后 import 载入的模块也会存放在 sys.modules 中。

sys.path[0]:执行 py 文件时会把文件所在的 directory 存入 sys.path[0] 中。直接执行存入的是绝对路径,带 -m 参数执行存入的是相对路径,交互式执行环境存入的是相对路径。

sys.path[1:] :第三方模块默认安装位置在环境变量 site-packages 中。

直接执行时 sys.path[0] 存入的是绝对路径,__name__ = ‘__main__’ ,此时如果使用相对导入会报错;带 -m 参数执行时 sys.path[0] 存入的是相对路径,__name__ = ‘package.subpackage.module’ ,此时可以使用相对导入。


#test.py
import sys
print(sys.path)

#直接执行 (作为 top-level 脚本,__name__ = '__main__')
python test.py
['C:\\test.py', 'D:\\SoftwareIT\\Python\\python37.zip', 'D:\\SoftwareIT\\Python\\DLLs', 'D:\\SoftwareIT\\Python\\lib', 'D:\\SoftwareIT\\Python', 'D:\\SoftwareIT\\Python\\lib\\site-packages']

#带-m参数执行 (作为 module,同import,__name__ = 'package.subpackage.module')
python -m test
['', 'D:\\SoftwareIT\\Python\\python37.zip', 'D:\\SoftwareIT\\Python\\DLLs', 'D:\\SoftwareIT\\Python\\lib', 'D:\\SoftwareIT\\Python', 'D:\\SoftwareIT\\Python\\lib\\site-packages']

#交互式执行环境
>>> import sys
>>> print(sys.path)
['', 'D:\\SoftwareIT\\Python\\python37.zip', 'D:\\SoftwareIT\\Python\\DLLs', 'D:\\SoftwareIT\\Python\\lib', 'D:\\SoftwareIT\\Python', 'D:\\SoftwareIT\\Python\\lib\\site-packages']


 

如下图,import os 时,在 sys.modules 中找到了 os 模块,停止搜索;不会继续在 sys.path[0] 中搜索同目录下的 os.py 文件,因此 os.getcwd() 没有报错。

from redis import Redis 时,在 sys.modules 中没有找到 redis 模块,继续搜索;按 sys.path[0] 的路径搜索到同目录下的 redis.py 文件,redis.py 文件没有 Redis 方法,因此报错。此时第三方库 redis 的路径 site-packages 在 sys.path[1:]  中,还没有执行搜索。


#目录结构
├── __init__.py
├── os.py
├── redis.py
└── test.py

#代码 test.py
import os
os.getcwd()
from redis import Redis

#结果 python test.py
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    from redis import Redis
ImportError: No module named redis


 

四、import

1.import A:

查找一个模块,如果有必要还会加载并初始化模块;

如果未找到该模块,则引发 ModuleNotFoundError ;

在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称,如果有 as 子句则使用其指定的名称,否则使用该属性的名称。

 

2.from A import B:

查找 from 子句中指定的模块,如有必要还会加载并初始化模块;

如果未找到该模块,则引发 ModuleNotFoundError ;

对于 import 子句中指定的每个标识符:检查被导入模块是否有该名称的属性,如果没有,尝试导入具有该名称的子模块,然后再次检查被导入模块是否有该属性;

如果未找到该属性,则引发 ImportError ;

否则的话,将对该值的引用存入局部命名空间,如果有 as 子句则使用其指定的名称,否则使用该属性的名称。

 

3.from A import *(不推荐使用):

仅在模块层级上被允许,如果在类或函数定义中使用它将引发 SyntaxError 。

在模块中定义的全部公有名称都将被绑定到局部命名空间。

模块的公有名称由模块命名空间中的 __all__ 的变量确定(包则由 __init__.py 文件的 __all__ 的变量确定), __all__ 是一个字符串列表 __all__ 。如果 __all__ 没有被定义,则公有名称是模块命名空间中所有不以 ‘_’ 开头的名称。

 

3.绝对导入和相对导入

(1)绝对导入:

import A.B ,from A import B 这种形式的 import 为绝对导入。

(2)相对导入:

from .A import B 这种形式的 import 为相对导入。

相对导入仅在包内导入时有效,包通常指包含 __init__.py 文件的目录。如 A 目录下没有 __init__.py 文件,则不可以使用相对导入。

一个前缀点号表示相对导入从当前包开始。 两个或更多前缀点号表示对当前包的上级包的相对导入,第一个点号之后的每个点号代表一级。

 

五、__import__()

1.定义:

内置函数,不用 import,在库中位置:importlib.__import__() 。


__import__(name, globals=None, locals=None, fromlist=(), level=0)


 

2.参数:

name 参数指定了导入的模块名。

globals 和 locals 参数确定了语句的上下文。

fromlist 参数给出了应该从由 name 参数指定的模块导入对象或子模块的名称。

level 参数指定了是使用绝对还是相对导入。0(默认值)表示绝对导入,正数表示相对导入,数值大小即为父目录层数


# import 语句与等价的 __import__() 语句
import spam
spam = __import__('spam', globals(), locals(), [], 0)

import spam.ham
spam = __import__('spam.ham', globals(), locals(), [], 0)

from spam.ham import eggs, sausage as saus
_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage


当 name 变量的形式为 pkg.mod 时,通常将会返回最高层级的包(第一个点号之前的名称),而不是以 name 命名的模块。 但是当给出了非空的 fromlist 参数时,则将返回以 name 命名的模块。

正常情况下一般不直接使用 __import__() 函数。

 

六、import_module()

1.定义:

非内置函数,需要 import,在库中位置:importlib.import_module() 。


import importlib
importlib.import_module(name, package=None)


 

2.参数:

name 参数指定了以绝对或相对导入方式导入什么模块(如 pkg.mod 或 ..mod )。

package 参数为包名。如果 name 参数为相对导入方式,则 package 参数不能为空(如 import_module(‘..mod’, ‘pkg.subpkg’) 将会导入 pkg.mod )。

import_module() 返回指定的包或模块(如 pkg.mod )。

import_module() 函数可以用于动态导入模块。

 

七、常见陷阱

1.循环导入

问题:模块A引用了模块B,模块B同时也引用了模块A。


#A.py
from .B import B_greet_back
def A_say_hello():
    print('A says hello!')
    B_greet_back()
def A_greet_back():
    print('A says hello back!')
if __name__ == '__main__':
    A_say_hello()

#B.py
from .A import A_greet_back
def B_say_hello():
    print('B says hello!')
    A_greet_back()
def B_greet_back():
    print('B says hello back!')
if __name__ == '__main__':
    B_say_hello()

#运行
python -m sample_package.B

#结果
ImportError: cannot import name 'A_greet_back'


如上图,sample_package 包里有 A 和 B 两个模块,当 B 执行时,会导入 A_greet_back ,导入时自动执行一遍 A ,而当 A 执行时,会导入 B_greet_back ,这样就出现了死循环。

解决方法:

(1)导入整个模块:将 from .A import A_greet_back 更改为 from . import A,这样就不会在 A 模块中去找 A_greet_back 的定义了,只需要确认 A 模块是否存在。

(2)延迟导入:将 from .A import A_greet_back 放入 B_say_hello() 中。

 

 

 

引用说明:

命名空间:https://www.cnblogs.com/windlaughing/archive/2013/05/26/3100362.html

作用域和闭包:https://www.cnblogs.com/linxiyue/p/7911916.html

导入机制:https://michael728.github.io/2018/12/15/python-package-import-order/

模块搜索路径:https://www.cnblogs.com/ljhdo/p/10674242.html

模块与包:https://zhuanlan.zhihu.com/p/30836117

绝对导入和相对导入:https://www.jianshu.com/p/5cc20b88bcf4

import陷阱:https://medium.com/pyladies-taiwan/python-%E7%9A%84-import-%E9%99%B7%E9%98%B1-3538e74f57e3

规避import的坑:https://zhuanlan.zhihu.com/p/78247846

 

转载请注明出处。

https://www.cnblogs.com/dawnredwood/p/python_import.html

Python量化投资网携手4326手游为资深游戏玩家推荐:《《封印战记》:锵锵~家族玩法的另一种打开方式~

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
Python
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论