Python:import语句的详细解析(绝对路径导入和相对路径导入)

日晨难再 2024-10-23 10:35:25 阅读 78

相关阅读

Python

icon-default.png?t=O83A

https://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函数改变了当前工作目录)。

<code># 例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后缀。由于是在导入的搜索目录中对import路径进行查找,所以同一个文件,如果在不同的搜索路径下导入,使用的import路径是不同的。

        图1展示了例3的文件结构,例4给出了几个import路径的示例,假设执行的是test1.py文件(因此'C:\\Users\\12078\\Desktop\\example'是sys.path列表的第一个元素)。

图1 文件结构 

<code># 例4

Package # 这是一个包的import路径

Package.__init__ # 这是一个.py文件的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路径为键,模块对象为值,如例5所示,假设执行的是test1.py文件。Python解释器根据import路径避免重复导入,注意这不是绝对路径,所以可能两个同名文件的import路径相同导致第二个导入无法进行,也可能同一个文件因为其具有不同的import路径被重复导入。

# 例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语句执行后,会在当前命名空间定义标识符,引用了被导入的模块,用于访问导入的模块(对于导入的包,指的是其__init__.py文件)中全局命名空间下的函数、类、变量等,需要注意的是导入是针对整个Python解释器而言的,而定义标识符是针对在当前命名空间而言的,两个步骤并不一定会全部完成(如例8的as子句和之后的from import语句所示)。Python解释器中所有导入的模块都会在sys.modules字典中创建键值对,使用它可以访问所有导入的模块(谨慎使用,因为这允许一个文件可以访问另一个文件中导入的模块)。

        例6中展示了在当前命名空间定义标识符的过程。

# 例6

# 文件test1.py

import math

import Package.test2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符Package和Package.test2

Package.func() # 可以用标识符调用__init__.py中的函数

Package.test2.func1() # 可以用标识符调用test2.py中的函数

import sys

sys.modules["Package"].func() # 可以用sys.modules字典来调用__init__.py中的函数

sys.modules["Package.test2"].func1() # 也可以用sys.modules字典来调用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

import sys

print(sys.modules["math"].sqrt(4)) # 使用sys可以让一个文件访问不是在该文件中导入的模块

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

2

This is test2.py

func in __init__.py

func1 in test2.py

func in __init__.py

func1 in test2.py

func in __init__.py

控制导入模块的执行

        如果希望某些代码在模块导入时不执行,则可以如例7所示,这利用了只有直接执行而非导入执行时,__name__变量才为"__main__"的性质。

# 例7

# 文件test1.py

import 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 sys

import Package.test2 as t2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符t2

# Package.func() 不可以调用__init__.py中的函数, 因为虽然Package包被导入, 但此时没有定义该标识符

sys.modules["Package"].func() # 此时只能用sys.modules字典来调用__init__.py中的函数

# Package.test2.func1() 不可以调用test2.py中的函数,因为虽然Package.test2模块被导入, 但此时没有定义该标识符

t2.func1() # 可以用新的标识符调用test2.py中的函数

sys.modules["Package.test2"].func1() # 也可以用sys.modules字典来调用test2.py中的函数

import Package.test2 # 由于Package包和Package.test2模块已被导入, 这导致直接在全局命名空间定义标识符Package和Package.test2

Package.func() # 用标识符来调用__init__.py中的函数

Package.test2.func1() # 用标识符来调用test2.py中的函数

# 文件__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

func1 in test2.py

func1 in test2.py

func in __init__.py

func1 in test2.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语句引入的标识符和sys.modules字典中的键值对,所以可以直接使用del语句删除import语句引入的标识符,然后del语句删除sys.modules字典中的值(这之后允许再次导入,因为解释器是根据sys.modules中的import路径决定是否能重新导入),只有删除了这两个引用,才能完全删除导入的模块,如例10所示。

# 例10

# 命令行

(test) C:\Users\12078\Desktop>python

>>> import Package

This is a package's __init__.py

>>> import sys

>>> sys.getrefcount(Package) # getrefcount函数可以得到一个对象的引用个数

3 # 三处引用分别来自于Packge标识符、sys.modules字典和getrefcount函数执行时临时添加的

>>> del sys.modules["Package"]

>>> sys.getrefcount(Package)

2

>>> del Package

from import语句

        首先按照简单的情况分析,即from后跟着的路径可以像之前一样是一个普通的import路径(为区别,称为绝对路径)。在文末会对相对路径导入进行讨论,这是from import语句特有的。

import路径

        与基本import语句一样,一个以.py后缀的文件或包(即目录)可以代表一个模块,对于import路径,路径分隔符不再是斜杠(对于Linux系统)或反斜杠(对于Windows系统)而是点,且对于.py后缀的文件不添加.py后缀。

模块导入

        根据import路径的不同,from import语句会有不同的效果,如import路径代表了一个包,则import后的必须是一个包(目录)内(注意不是__init__.py文件中)的模块;如import路径代表了一个.py文件,则import后的必须是该文件内的全局命名空间下的函数、类、变量等。

        与基本import语句相同,所有import路径上的目录(包)会从浅到深依次导入(执行),对于import路径代表了一个包,包内的模块是最后导入(执行)的。所有导入的模块都会在sys.modules字典(它和sys.path一样属于整个Python解释器)中以添加模块的import路径为键,模块对象为值。

定义标识符

        如果import后的是一个包内的模块,会在当前命名空间定义标识符,引用了import后被导入的模块(注意,虽然所有import路径上的目录(包)会从浅到深依次导入,但不会定义标识符),用于访问导入的模块(对于导入的包,指的是其__init__.py文件)中全局命名空间下的函数、类、变量等;如果import路径代表了一个.py文件,则import后的函数、类、变量标识符会被引入,如例11所示。如果import后是*,代表包内的所有模块或py文件内全局命名空间的所有标识符

# 例11

# 文件test1.py

from Package import test2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符test2

# 上面的导入的效果就像是 import Package.test2 as test2, 导入(执行)了Package包但没有定义Package标识符

# Package.func() 不可以调用__init__.py中的函数, 因为虽然Package包被导入, 但此时没有定义该标识符

import sys

sys.modules["Package"].func() # 此时只能用sys.modules字典来调用__init__.py中的函数

from Package.test2 import func1 # 在全局命名空间定义标识符func1

from Package.test2 import func2 # 在全局命名空间定义标识符func2

func1()

func2()

# 文件__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\example>python test1.py

​func in __init__.py

func1 in test2.py

func2 in test2.py

as子句

        这部分的与基本import语句相同。

多重from import语句

        多个from import语句可以合并为同一个语句,当语句包含多个子句(由逗号分隔,如果用可选的小括号包围时结尾可以有多余的逗号)时如同这些子句被分成独立的import语句一样,例12重写了例11。

​# 例12

# 文件test1.py

from Package.test2 import func1, func2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符func1和func2

# from Package.test2 import (func1, func2) 等价表示

# from Package.test2 import (func1, func2,) 等价表示

func1()

func2()

# 文件__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\example>python test1.py

​func in __init__.py

func1 in test2.py

func2 in test2.py

删除导入的模块

        这部分的与基本import语句相同。

相对路径导入

        相对导入是一个难点,本节将详细解释。

        首先忘掉sys.path列表,相对路径导入并不是在导入的搜索目录中进行搜索。我们将前面带有若干点.的路径和只由若干点组成的路径称为相对路径。

        当一个模块还未完全被导入(执行)时,其实就能在sys.modules中看到它了,如例13所示。

​# 例13

# 文件test1.py

from Package.test2 import func1, func2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符func1和func2

# 文件__init__.py

def func():

print("func in __init__.py")

if __name__ == "__main__":

print("This is a package's __init__.py")

# 文件test2.py

import sys

print(sys.modules)

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\example>python test1.py

        这和相对路径导入有什么关系呢?其实相对导入是通过sys.modules中本模块的import路径反查绝对路径来查找的。有一点需要注意,绝对路径的层级不能高于本模块的import路径的第一级。这也许听起来令人费解,例14展示了一个具体的例子。

​# 例14

# 文件test1.py

from Package.test2 import func1, func2 # 首先导入(执行)Package包, 随后导入(执行)Package.test2模块, 最后在全局命名空间定义标识符func1和func2

# 文件__init__.py

def func():

print("func in __init__.py")

if __name__ == "__main__":

print("This is a package's __init__.py")

# 文件test2.py

import sys

print(sys.modules)

print(sys.path)

del sys.path[0]

print(sys.path)

from .test3 import *

from . import test3

# from ..Package.test3 import * 报错:ImportError: attempted relative import beyond top-level package

# from .. import Package 报错:ImportError: attempted relative import beyond top-level package

print(sys.modules)

def func1():

print("func1 in test2.py")

def func2():

print("func2 in test2.py")

if __name__ == "__main__":

print("This is test2.py")

# 文件test3.py(与test2.py一起在Package中)

def func3():

print("func3 in test3.py")

# 命令行

(test) C:\Users\12078\Desktop\example>python test1.py

{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (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 'importlib._bootstrap_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from 'D:\\Anaconda\\lib\\codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'D:\\Anaconda\\lib\\encodings\\aliases.py'>, 'encodings': <module 'encodings' from 'D:\\Anaconda\\lib\\encodings\\__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'D:\\Anaconda\\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\\lib\\encodings\\gbk.py'>, '_signal': <module '_signal' (built-in)>, 'encodings.latin_1': <module 'encodings.latin_1' from 'D:\\Anaconda\\lib\\encodings\\latin_1.py'>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from 'D:\\Anaconda\\lib\\abc.py'>, 'io': <module 'io' from 'D:\\Anaconda\\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\\lib\\stat.py'>, '_collections_abc': <module '_collections_abc' from 'D:\\Anaconda\\lib\\_collections_abc.py'>, 'genericpath': <module 'genericpath' from 'D:\\Anaconda\\lib\\genericpath.py'>, 'ntpath': <module 'ntpath' from 'D:\\Anaconda\\lib\\ntpath.py'>, 'os.path': <module 'ntpath' from 'D:\\Anaconda\\lib\\ntpath.py'>, 'os': <module 'os' from 'D:\\Anaconda\\lib\\os.py'>, '_sitebuiltins': <module '_sitebuiltins' from 'D:\\Anaconda\\lib\\_sitebuiltins.py'>, '_locale': <module '_locale' (built-in)>, '_bootlocale': <module '_bootlocale' from 'D:\\Anaconda\\lib\\_bootlocale.py'>, '_distutils_hack': <module '_distutils_hack' from 'D:\\Anaconda\\lib\\site-packages\\_distutils_hack\\__init__.py'>, 'types': <module 'types' from 'D:\\Anaconda\\lib\\types.py'>, 'importlib._bootstrap': <module 'importlib._bootstrap' (frozen)>, 'importlib._bootstrap_external': <module 'importlib._bootstrap_external' (frozen)>, 'warnings': <module 'warnings' from 'D:\\Anaconda\\lib\\warnings.py'>, 'importlib': <module 'importlib' from 'D:\\Anaconda\\lib\\importlib\\__init__.py'>, 'importlib.machinery': <module 'importlib.machinery' from 'D:\\Anaconda\\lib\\importlib\\machinery.py'>, '_heapq': <module '_heapq' (built-in)>, 'heapq': <module 'heapq' from 'D:\\Anaconda\\lib\\heapq.py'>, 'itertools': <module 'itertools' (built-in)>, 'keyword': <module 'keyword' from 'D:\\Anaconda\\lib\\keyword.py'>, '_operator': <module '_operator' (built-in)>, 'operator': <module 'operator' from 'D:\\Anaconda\\lib\\operator.py'>, 'reprlib': <module 'reprlib' from 'D:\\Anaconda\\lib\\reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'collections': <module 'collections' from 'D:\\Anaconda\\lib\\collections\\__init__.py'>, 'collections.abc': <module 'collections.abc' from 'D:\\Anaconda\\lib\\collections\\abc.py'>, '_functools': <module '_functools' (built-in)>, 'functools': <module 'functools' from 'D:\\Anaconda\\lib\\functools.py'>, 'contextlib': <module 'contextlib' from 'D:\\Anaconda\\lib\\contextlib.py'>, 'enum': <module 'enum' from 'D:\\Anaconda\\lib\\enum.py'>, '_sre': <module '_sre' (built-in)>, 'sre_constants': <module 'sre_constants' from 'D:\\Anaconda\\lib\\sre_constants.py'>, 'sre_parse': <module 'sre_parse' from 'D:\\Anaconda\\lib\\sre_parse.py'>, 'sre_compile': <module 'sre_compile' from 'D:\\Anaconda\\lib\\sre_compile.py'>, 'copyreg': <module 'copyreg' from 'D:\\Anaconda\\lib\\copyreg.py'>, 're': <module 're' from 'D:\\Anaconda\\lib\\re.py'>, 'typing.io': <class 'typing.io'>, 'typing.re': <class 'typing.re'>, 'typing': <module 'typing' from 'D:\\Anaconda\\lib\\typing.py'>, 'importlib.abc': <module 'importlib.abc' from 'D:\\Anaconda\\lib\\importlib\\abc.py'>, 'importlib.util': <module 'importlib.util' from 'D:\\Anaconda\\lib\\importlib\\util.py'>, 'mpl_toolkits': <module 'mpl_toolkits' (namespace)>, 'sphinxcontrib': <module 'sphinxcontrib' (namespace)>, 'zope': <module 'zope' from 'D:\\Anaconda\\lib\\site-packages\\zope\\__init__.py'>, 'site': <module 'site' from 'D:\\Anaconda\\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'>}

['C:\\Users\\12078\\Desktop\\example', 'D:\\Anaconda\\python39.zip', 'D:\\Anaconda\\DLLs', 'D:\\Anaconda\\lib', 'D:\\Anaconda', 'D:\\Anaconda\\lib\\site-packages', 'D:\\Anaconda\\lib\\site-packages\\win32', 'D:\\Anaconda\\lib\\site-packages\\win32\\lib', 'D:\\Anaconda\\lib\\site-packages\\Pythonwin']

['D:\\Anaconda\\python39.zip', 'D:\\Anaconda\\DLLs', 'D:\\Anaconda\\lib', 'D:\\Anaconda', 'D:\\Anaconda\\lib\\site-packages', 'D:\\Anaconda\\lib\\site-packages\\win32', 'D:\\Anaconda\\lib\\site-packages\\win32\\lib', 'D:\\Anaconda\\lib\\site-packages\\Pythonwin']

{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (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 'importlib._bootstrap_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from 'D:\\Anaconda\\lib\\codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'D:\\Anaconda\\lib\\encodings\\aliases.py'>, 'encodings': <module 'encodings' from 'D:\\Anaconda\\lib\\encodings\\__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'D:\\Anaconda\\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\\lib\\encodings\\gbk.py'>, '_signal': <module '_signal' (built-in)>, 'encodings.latin_1': <module 'encodings.latin_1' from 'D:\\Anaconda\\lib\\encodings\\latin_1.py'>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from 'D:\\Anaconda\\lib\\abc.py'>, 'io': <module 'io' from 'D:\\Anaconda\\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\\lib\\stat.py'>, '_collections_abc': <module '_collections_abc' from 'D:\\Anaconda\\lib\\_collections_abc.py'>, 'genericpath': <module 'genericpath' from 'D:\\Anaconda\\lib\\genericpath.py'>, 'ntpath': <module 'ntpath' from 'D:\\Anaconda\\lib\\ntpath.py'>, 'os.path': <module 'ntpath' from 'D:\\Anaconda\\lib\\ntpath.py'>, 'os': <module 'os' from 'D:\\Anaconda\\lib\\os.py'>, '_sitebuiltins': <module '_sitebuiltins' from 'D:\\Anaconda\\lib\\_sitebuiltins.py'>, '_locale': <module '_locale' (built-in)>, '_bootlocale': <module '_bootlocale' from 'D:\\Anaconda\\lib\\_bootlocale.py'>, '_distutils_hack': <module '_distutils_hack' from 'D:\\Anaconda\\lib\\site-packages\\_distutils_hack\\__init__.py'>, 'types': <module 'types' from 'D:\\Anaconda\\lib\\types.py'>, 'importlib._bootstrap': <module 'importlib._bootstrap' (frozen)>, 'importlib._bootstrap_external': <module 'importlib._bootstrap_external' (frozen)>, 'warnings': <module 'warnings' from 'D:\\Anaconda\\lib\\warnings.py'>, 'importlib': <module 'importlib' from 'D:\\Anaconda\\lib\\importlib\\__init__.py'>, 'importlib.machinery': <module 'importlib.machinery' from 'D:\\Anaconda\\lib\\importlib\\machinery.py'>, '_heapq': <module '_heapq' (built-in)>, 'heapq': <module 'heapq' from 'D:\\Anaconda\\lib\\heapq.py'>, 'itertools': <module 'itertools' (built-in)>, 'keyword': <module 'keyword' from 'D:\\Anaconda\\lib\\keyword.py'>, '_operator': <module '_operator' (built-in)>, 'operator': <module 'operator' from 'D:\\Anaconda\\lib\\operator.py'>, 'reprlib': <module 'reprlib' from 'D:\\Anaconda\\lib\\reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'collections': <module 'collections' from 'D:\\Anaconda\\lib\\collections\\__init__.py'>, 'collections.abc': <module 'collections.abc' from 'D:\\Anaconda\\lib\\collections\\abc.py'>, '_functools': <module '_functools' (built-in)>, 'functools': <module 'functools' from 'D:\\Anaconda\\lib\\functools.py'>, 'contextlib': <module 'contextlib' from 'D:\\Anaconda\\lib\\contextlib.py'>, 'enum': <module 'enum' from 'D:\\Anaconda\\lib\\enum.py'>, '_sre': <module '_sre' (built-in)>, 'sre_constants': <module 'sre_constants' from 'D:\\Anaconda\\lib\\sre_constants.py'>, 'sre_parse': <module 'sre_parse' from 'D:\\Anaconda\\lib\\sre_parse.py'>, 'sre_compile': <module 'sre_compile' from 'D:\\Anaconda\\lib\\sre_compile.py'>, 'copyreg': <module 'copyreg' from 'D:\\Anaconda\\lib\\copyreg.py'>, 're': <module 're' from 'D:\\Anaconda\\lib\\re.py'>, 'typing.io': <class 'typing.io'>, 'typing.re': <class 'typing.re'>, 'typing': <module 'typing' from 'D:\\Anaconda\\lib\\typing.py'>, 'importlib.abc': <module 'importlib.abc' from 'D:\\Anaconda\\lib\\importlib\\abc.py'>, 'importlib.util': <module 'importlib.util' from 'D:\\Anaconda\\lib\\importlib\\util.py'>, 'mpl_toolkits': <module 'mpl_toolkits' (namespace)>, 'sphinxcontrib': <module 'sphinxcontrib' (namespace)>, 'zope': <module 'zope' from 'D:\\Anaconda\\lib\\site-packages\\zope\\__init__.py'>, 'site': <module 'site' from 'D:\\Anaconda\\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'>, 'Package.test3': <module 'Package.test3' from 'C:\\Users\\12078\\Desktop\\example\\Package\\test3.py'>}

        import路径前的一个点表示当前模块的包(该例中指Package目录)内,而两个点表示所处的包的父包(该例中指example目录)内,以此类推,只使用一个点表示表示当前模块的包,而两个点表示当前模块所处的包的父包,以此类推。

        文件一中使用from import语句和绝对路径导入了test2.py文件,在test2.py文件中,首先将sys.path中的第一个元素删除(这是为了确保相对导入与sys.path无关),随后使用了相对路径导入,其中前两种是正确的,而后两种是错误的,因为它们使用两个点访问了..(该例中指example目录),而test2.py的import路径是Package.test2,两个点已经高于Package目录了。 这就意味着,绝对导入在某种情况下是错误的,比如在例14中,直接执行test2.py文件是错误的(python test2.py),因为此时test2模块的import路径将会是test2而一个点已经高于它了。

        这看起来很神奇,test2.py文件是否能使相对路径导入同目录的其他文件,取决于其运行方式。这也决定了相对导入是不可能单独使用的,它需要知道当前模块的import路径。

        例14最后的结果显示,test3.py文件被导入后的import路径是Package.test3,这其实也是直接通过test2.py的import路径推断出来的,而与sys.path无关。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。