相关阅读
Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm=1001.2014.3001.5482
import语句是Python中一个很重要的机制,允许在一个文件中访问另一个文件的函数、类、变量等,本文就将进行详细介绍。
在具体谈论import语句前,首先介绍相关的前置知识——导入的搜索目录。
导入的搜索目录
既然要求在一个文件访问其他文件,那么如何找到其他文件就是一个问题,这由sys包中的path列表决定,它在Python解释器在启动时会自动初始化,并属于整个Python解释器而不是某个文件。
初始化后的sys.path列表由多个元素组成,每个元素都是一个字符串,表示在导入时会依次搜索的路径(除内建模块外,比如math)。
sys.path列表第一个元素会根据Python解释器的启动方式而定:
1、如果是以交互式启动,则会被设置为空字符串,以表示当前工作目录,如例1所示(空字符串永远代表当前工作目录,即使在交互式Python中使用os.chdir函数改变了当前工作目录)。
# 例1
# 命令行
(test) C:\Users\12078\Desktop>python
Python 3.10.14 | packaged by Anaconda, Inc. | (main, May 6 2024, 19:44:50) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', 'D:\\Anaconda\\envs\\test\\python310.zip', 'D:\\Anaconda\\envs\\test\\DLLs', 'D:\\Anaconda\\envs\\test\\lib', 'D:\\Anaconda\\envs\\test', 'D:\\Anaconda\\envs\\test\\lib\\site-packages']
2、如果以python -m命令启动,则会被设置当前工作目录,如例2所示(注意其与例1的区别)。
# 例2
# 文件test1.py
import sys
print(sys.path)# 命令行
(test) C:\Users\12078\Desktop>python -m example.test1
['C:\\Users\\12078\\Desktop', 'D:\\Anaconda\\envs\\test\\python310.zip', 'D:\\Anaconda\\envs\\test\\DLLs', 'D:\\Anaconda\\envs\\test\\lib', 'D:\\Anaconda\\envs\\test', 'D:\\Anaconda\\envs\\test\\lib\\site-packages']
3、如果是以普通的python命令启动(最常见),则会被设置为命令行中脚本文件所在的目录,如例3所示(本文将这种情况为例)。
# 例3
# 文件test1.py
import sys
print(sys.path)# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
['C:\\Users\\12078\\Desktop\\example', 'D:\\Anaconda\\envs\\test\\python310.zip', 'D:\\Anaconda\\envs\\test\\DLLs', 'D:\\Anaconda\\envs\\test\\lib', 'D:\\Anaconda\\envs\\test', 'D:\\Anaconda\\envs\\test\\lib\\site-packages']
sys.path列表中的其他元素还包括环境变量PYTHONPATH中指定的目录、标准库路径、site-packages目录等。该列表可以在程序执行时被添加、删除元素,就像一个普通的列表那样,但是这是需要谨慎,因为可能出现导入相关的问题。
import语句的语法
下面是Python官方文档中,对于import语句的描述,由于imort语句分为基本import语句和from import语句,下面将分别讨论。
import_stmt ::= "import" module ["as" identifier] ("," module ["as" identifier])*| "from" relative_module "import" identifier ["as" identifier]("," identifier ["as" identifier])*| "from" relative_module "import" "(" identifier ["as" identifier]("," identifier ["as" identifier])* [","] ")"| "from" relative_module "import" "*"
module ::= (identifier ".")* identifier
relative_module ::= "."* module | "."+
基本import语句
基本import语句的执行分两步进行,首先会在导入的搜索目录中,根据模块的import路径查找并导入(如果已导入,则不会重复导入),随后在import语句执行位置所在的命名空间定义若干标识符。
import路径
一个以.py后缀的文件或包(即目录)可以代表一个模块,对于import路径,路径分隔符不再是斜杠(对于Linux系统)或反斜杠(对于Windows系统)而是点,且对于.py后缀的文件不添加.py后缀。
图1展示了例3的文件结构,例4给出了几个import路径的示例,假设执行的是test1.py文件(因此'C:\\Users\\12078\\Desktop\\example'是sys.path列表的第一个元素)。
图1 文件结构
# 例4
Package # 这是一个包的import路径
Package.__init__ # 这是一个包的import路径, 与上等价
Package.test2 # 这是一个.py文件的import路径
example.Package # 这是不合法的, 要相对sys.path中的路径
example.Package.test2 # 这是不合法的, 要相对sys.path中的路径
Package.test2.func1 # 这是不合法的, 一个函数不是模块
模块导入
导入一个模块,会执行模块内的所有代码(包也是模块,对于它是指执行了包中的__init__.py文件),并在sys.modules字典(它和sys.path一样属于整个Python解释器)中记录模块的import路径和模块的绝对路径(Python解释器根据import路径防止重复导入),如例5所示,假设执行的是test1.py文件。
# 例5
# 文件test1.py
import sys
print(sys.modules)
import Package.test2
print(sys.modules)
import Package# 文件__init__.py
def fun():print("func in __init__.py")
print("This is a package's __init__.py")# 文件test2.py
def fun1c():print("func1 in test2.py")
def fun2c():print("func2 in test2.py")print("This is test2.py")# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_io': <module '_io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>, 'winreg': <module 'winreg' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from 'D:\\Anaconda\\envs\\test\\lib\\codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\aliases.py'>, 'encodings': <module 'encodings' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\utf_8.py'>, '_codecs_cn': <module '_codecs_cn' (built-in)>, '_multibytecodec': <module '_multibytecodec' (built-in)>, 'encodings.gbk': <module 'encodings.gbk' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\gbk.py'>, '_signal': <module '_signal' (built-in)>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from 'D:\\Anaconda\\envs\\test\\lib\\abc.py'>, 'io': <module 'io' from 'D:\\Anaconda\\envs\\test\\lib\\io.py'>, '__main__': <module '__main__' from 'C:\\Users\\12078\\Desktop\\example\\test1.py'>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from 'D:\\Anaconda\\envs\\test\\lib\\stat.py'>, '_collections_abc': <module '_collections_abc' from 'D:\\Anaconda\\envs\\test\\lib\\_collections_abc.py'>, 'genericpath': <module 'genericpath' from 'D:\\Anaconda\\envs\\test\\lib\\genericpath.py'>, '_winapi': <module '_winapi' (built-in)>, 'ntpath': <module 'ntpath' from 'D:\\Anaconda\\envs\\test\\lib\\ntpath.py'>, 'os.path': <module 'ntpath' from 'D:\\Anaconda\\envs\\test\\lib\\ntpath.py'>, 'os': <module 'os' from 'D:\\Anaconda\\envs\\test\\lib\\os.py'>, '_sitebuiltins': <module '_sitebuiltins' from 'D:\\Anaconda\\envs\\test\\lib\\_sitebuiltins.py'>, '_distutils_hack': <module '_distutils_hack' from 'D:\\Anaconda\\envs\\test\\lib\\site-packages\\_distutils_hack\\__init__.py'>, 'site': <module 'site' from 'D:\\Anaconda\\envs\\test\\lib\\site.py'>}
This is a package's __init__.py
This is test2.py
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_io': <module '_io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>, 'winreg': <module 'winreg' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from 'D:\\Anaconda\\envs\\test\\lib\\codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\aliases.py'>, 'encodings': <module 'encodings' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\utf_8.py'>, '_codecs_cn': <module '_codecs_cn' (built-in)>, '_multibytecodec': <module '_multibytecodec' (built-in)>, 'encodings.gbk': <module 'encodings.gbk' from 'D:\\Anaconda\\envs\\test\\lib\\encodings\\gbk.py'>, '_signal': <module '_signal' (built-in)>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from 'D:\\Anaconda\\envs\\test\\lib\\abc.py'>, 'io': <module 'io' from 'D:\\Anaconda\\envs\\test\\lib\\io.py'>, '__main__': <module '__main__' from 'C:\\Users\\12078\\Desktop\\example\\test1.py'>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from 'D:\\Anaconda\\envs\\test\\lib\\stat.py'>, '_collections_abc': <module '_collections_abc' from 'D:\\Anaconda\\envs\\test\\lib\\_collections_abc.py'>, 'genericpath': <module 'genericpath' from 'D:\\Anaconda\\envs\\test\\lib\\genericpath.py'>, '_winapi': <module '_winapi' (built-in)>, 'ntpath': <module 'ntpath' from 'D:\\Anaconda\\envs\\test\\lib\\ntpath.py'>, 'os.path': <module 'ntpath' from 'D:\\Anaconda\\envs\\test\\lib\\ntpath.py'>, 'os': <module 'os' from 'D:\\Anaconda\\envs\\test\\lib\\os.py'>, '_sitebuiltins': <module '_sitebuiltins' from 'D:\\Anaconda\\envs\\test\\lib\\_sitebuiltins.py'>, '_distutils_hack': <module '_distutils_hack' from 'D:\\Anaconda\\envs\\test\\lib\\site-packages\\_distutils_hack\\__init__.py'>, 'site': <module 'site' from 'D:\\Anaconda\\envs\\test\\lib\\site.py'>, 'Package': <module 'Package' from 'C:\\Users\\12078\\Desktop\\example\\Package\\__init__.py'>, 'Package.test2': <module 'Package.test2' from 'C:\\Users\\12078\\Desktop\\example\\Package\\test2.py'>}
可以看出,在test2.py文件(导入)执行前,Package包中的__init__.py文件先(导入)执行了,因为所有import路径上的目录(包)会从浅到深依次导入,最后导入目标模块,从导入后的sys.modules字典中也可以看到Package包比Package.test2模块添加得更早。如果在此之后,再次导入Package,__init__.py文件不会再(导入)执行了,因为Python解释器会避免重复导入。
定义标识符
在import语句执行后,会在当前命名空间定义标识符,用于访问导入的模块中全局命名空间下的函数、类、变量等,需要注意的是导入是针对整个Python解释器而言的,而定义标识符是针对在当前命名空间而言的,两个步骤并不一定会全部完成(如例8所示)。
例6中展示了在当前命名空间定义标识符的过程。
# 例6
# 文件test1.pyimport Package.test2 # 首先导入(执行)Package包,随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符Package和Package.test2
Package.func() # 可以调用__init__.py中的函数了
Package.test2.func1() # 可以调用test2.py中的函数了def ttt():Package.func() # 局部命名空间可以访问全局命名空间的标识符ttt() # 调用ttt函数# 文件__init__.py
def func():print("func in __init__.py")
print("This is a package's __init__.py")# 文件test2.py
def func1():print("func1 in test2.py")
def func2():print("func2 in test2.py")print("This is test2.py")# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
This is a package's __init__.py
This is test2.py
func in __init__.py
fun1c in test2.py
func in __init__.py
控制导入模块的执行
如果希望某些代码在模块导入时不执行,则可以如例7所示,这利用了只有直接执行而非导入执行时,__name__变量才为"__main__"的性质。
# 例7
# 文件test1.pyimport Package.test2 # 首先导入(执行)Package包,随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符Package和Package.test2
Package.func() # 可以调用__init__.py中的函数了
Package.test2.func1() # 可以调用test2.py中的函数了def ttt():Package.func() # 局部命名空间可以访问全局命名空间的标识符ttt() # 调用ttt函数# 文件__init__.py
def func():print("func in __init__.py")if __name__ == "__main__":print("This is a package's __init__.py")# 文件test2.py
def func1():print("func1 in test2.py")
def func2():print("func2 in test2.py")if __name__ == "__main__":print("This is test2.py")# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
func in __init__.py
fun1c in test2.py
func in __init__.py
as子句
如果模块的路径很长,则定义的标识符也会很长,此时使用as子句就可以用指定的标识符代表该模块,如例8所示,需要小心这可能会导致标识符的覆盖问题。
# 例8
# 文件test1.py
import Package.test2 as t2 # 首先导入(执行)Package包,随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符t2
# Package.func() 不可以调用__init__.py中的函数,因为虽然Package包被导入,但此时没有定义任何标识符
# Package.test2.func1() 不可以调用test2.py中的函数,因为虽然Package.test2模块被导入,但此时没有定义该标识符
t2.func1() # 需要用新的标识符调用test2.py中的函数import Package.test2 # 由于Package包和Package.test2模块已被导入,直接在全局命名空间定义标识符Package和Package.test2
Package.func()
Package.test2.func1()# 文件__init__.py
def func():print("func in __init__.py")if __name__ == "__main__":print("This is a package's __init__.py")# 文件test2.py
def func1():print("func1 in test2.py")
def func2():print("func2 in test2.py")if __name__ == "__main__":print("This is test2.py")# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
func in __init__.py
fun1c in test2.py
func in __init__.py
多重import语句
多个import语句可以合并为同一个语句,当语句包含多个子句(由逗号分隔)时这两个步骤将对每个子句分别执行,如同这些子句被分成独立的import语句一样,例9重写了例8。
# 例9
# 文件test1.py
import Package.test2 as t2, Package.test2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 接着在全局命名空间定义标识符t2, 最后在全局命名空间定义标识符Package和Package.test2
t2.func1() # 用新的标识符调用test2.py中的函数
Package.func()
Package.test2.func1()# 文件__init__.py
def func():print("func in __init__.py")if __name__ == "__main__":print("This is a package's __init__.py")# 文件test2.py
def func1():print("func1 in test2.py")
def func2():print("func2 in test2.py")if __name__ == "__main__":print("This is test2.py")# 命令行
(test) C:\Users\12078\Desktop>python .\example\test1.py
fun1c in test2.py
func in __init__.py
fun1c in test2.py
至此,所有关于基本import语句的内容就结束了,由于篇幅问题,from import语句将在下一篇文章中讨论。