python--基础知识点--导入模块

1. 包的概念和结构

当一个项目中有很多模块时,需要再进行组织时。可将模块功能类似的模块放到一起形成“包”。本质上,“包”就是一个必须有__init__.py文件的文件夹。

包下面可以包含“模块”,也可以再包含“子包”。就像文件夹下可以有文件也可以有子文件夹一样。

__init__.py文件的作用:

  • 包的标识,区别于普通文件夹。
  • 实现模糊导入,就是使用import * 导包。
  • 导入包实际执行的是__init__.py文件,可以在这个__init__.py文件中做这个包的初始化,以及需要统一执行的代码。

2. 包的使用

2.1 普通导入和模糊导入(*)导入

示例:
包目录如下图所示
在这里插入图片描述

2.1.1 普通导入
# hello.py
class AddSub:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a+self.b

    def sub(self):
        return self.a-self.b
# hello1.py
def add(a, b):
    return a + b


def sub(a, b):
    return a - b
# 123.py
from my_test import hello, hello1

test0 = hello.AddSub(2, 3)
print(test0.add())
print(test0.sub())
test1_add = hello1.add(2, 3)
test1_sub = hello1.sub(2, 3)
print(test1_add)
print(test1_sub)

"""
运行结果:
5
-1
5
-1

Process finished with exit code 0
"""
2.1.2 模糊导入(*)导入包

import * 这样的语句理论上时希望文件系统找出保重所有的子模块,然后导入它们。这可能会花费长时间等待。Python解决方案是提供一个明确的包索引。

这个索引由__init__.py定义,该变量为一列表。

以下是使用__init__.py进行模糊导入的示例:

# hello.py
class AddSub:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a+self.b

    def sub(self):
        return self.a-self.b
# hello1.py
def add(a, b):
    return a + b


def sub(a, b):
    return a - b
# __init__.py
__all__ = ["hello", "hello1"]
# 123.py
from my_test import *

test0 = hello.AddSub(2, 3)
print(test0.add())
print(test0.sub())
test1_add = hello1.add(2, 3)
test1_sub = hello1.sub(2, 3)
print(test1_add)
print(test1_sub)

"""
运行结果:
5
-1
5
-1

Process finished with exit code 0
"""
2.2 绝对导入与 相对导入

相对导入,使用“.”和“..”,例如:

  • from . import module_name
  • from … import module_name

绝对导入,不使用“.”和“..”的其它所有情况。

2.2.1 绝对导入

在这里插入图片描述
绝对导入时解释器判断一个文件夹是包而不是普通文件夹必须满足一个条件:

  • 该文件夹下必须有__init__.py文件。
2.2.1.1 绝对导入时遇错

不使用“.”和“..”的其它所有情况本质上都属于绝对导入,都需要在sys.path默认的搜索路径(有多个路径)中按一定的顺序查找导入的模块

错误

  • ModuleNotFoundError: No module named 'xxxx'

错误示例:
所用包与模块的结构如下图所示:
在这里插入图片描述
#pic_center)

# hello2.py
import hello3
# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# main.py
from my_test import hello2  
# 由于sys.path默认的模块搜索路径中不存在路径“E:\Project\PyCharm\test\import_找不到被导模块\my_test”,导致找不到hello3模块
# 主目录为:“E:\Project\PyCharm\test”
# 当前目录:“\Project\PyCharm\test\import_找不到被导模块”

以main.py作为主函数入口,运行main.py

"""
运行结果:
Traceback (most recent call last):
  File "E:/Project/PyCharm/test/import_找不到被导模块/main.py", line 13, in <module>
    from my_test import hello2
  File "E:\Project\PyCharm\test\import_找不到被导模块\my_test\hello2.py", line 1, in <module>
    import hello3
ModuleNotFoundError: No module named 'hello3'

Process finished with exit code 1
"""

解决方案

(1) 使用导入时完整路径(绝对路径)

# 将hello2.py中原来代码修改成以下代码
from import_找不到被导模块.my_test import hello3

# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# main.py
from my_test import hello2  

以main.py作为主函数入口,运行main.py

"""
运行代码:
import_找不到被导模块\my_test\hello3.py

Process finished with exit code 0
"""

(2) 使用sys.path.append(“需要添加的路径”)

# hello2.py
import hello3
# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# 将main.py中原来代码修改成以下代码
import sys
sys.path.append(r"E:\Project\PyCharm\test\import_找不到被导模块\my_test")
from my_test import hello2

以main.py作为主函数入口,运行main.py

"""
运行代码:
import_找不到被导模块\my_test\hello3.py

Process finished with exit code 0
"""
2.2.2 相对导入

相对导入时解释器判断一个文件夹是包而不是普通文件夹必须满足两个条件:

  • 该文件夹下必须有__init__.py文件。
  • 该文件夹下不能含有顶层模块(含有主函数的模块)【这条很重要,往往被忽略】。
2.2.2.1 相对导入错误1

错误1.1

  • ModuleNotFoundError: No module named '__main__.mul_div'; '__main__' is not a package

错误示例:
所用包与模块的结构如下图所示:
在这里插入图片描述

# mul_div.py
class MuLDiv:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def mul(self):
        return self.a*self.b

    def div(self):
        return self.a/self.b
# main_0.py
from .mul_div import MuLDiv


mul_div = MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())

以main_0.py作为主函数入口,运行main_0.py

"""
运行结果:
Traceback (most recent call last):
  File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0.py", line 1, in <module>
    from .mul_div import MuLDiv
ModuleNotFoundError: No module named '__main__.mul_div'; '__main__' is not a package

Process finished with exit code 1
"""

错误1.2

  • ImportError: cannot import name ‘mul_div’
# mul_div.py
class MuLDiv:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def mul(self):
        return self.a*self.b

    def div(self):
        return self.a/self.b
# 将main_0中代码改为
from . import mul_div


mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())

以main_0.py作为主函数入口,运行main_0.py

"""
运行结果:
Traceback (most recent call last):
  File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0.py", line 1, in <module>
    from . import mul_div
ImportError: cannot import name 'mul_div'

Process finished with exit code 1
"""

错误1.1和错误1.2分析:

错误1.1和错误1.2本质是同一个问题导致。

相对导入基于当前模块的名称,因为主模块总被命名为"__main__"。当我们从主模块启动时,Python就识图用"__main__“替换”.",于是from . import mul_div实际变成了from __mian__ import mul_div ,而from .mul_div import MulDiv 实际变成了from __main__.mul_div import MulDiv,这当然是找不到的。

尝试解决错误1.1和错误1.2,此时遇到问题1.3,过程如下:

问题1.3

  • ImportError: attempted relative import with no known parent package

错误示例:
所用包与模块的结构如下图所示:
在这里插入图片描述
在sub_1_my_test包下新建主函数入口模块main_0_0.py

# mul_div.py
class MuLDiv:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def mul(self):
        return self.a*self.b

    def div(self):
        return self.a/self.b
# 为了测试解决错误1.1和错误1.2,此处将main_0.py代码修改成
from . import mul_div
from .mul_div import MuLDiv


mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())

mul_div = MuLDiv(6, 2)
print(mul_div.mul())
print(mul_div.div())
# main_0_0.py
import main_0

以main_0_0.py作为主函数入口,运行min_0_0.py

"""
运行结果:
Traceback (most recent call last):
  File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0_0.py", line 1, in <module>
    import main_0
  File "E:\Project\PyCharm\test\relative_import_test\my_test\sub_1_my_test\main_0.py", line 1, in <module>
    from . import mul_div
ImportError: attempted relative import with no known parent package

Process finished with exit code 1
"""

问题分析错误1.3:
忽略了相对导入时一个文件夹成为包的一个条件–该文件夹下不能含有顶层模块(含有主函数的模块)。此处是因为“.”导入时需要将sub_1_my_test看作一个包,但由于主函数的入口main_0_0.py在sub_1_my_test下,导致sub_1_my_test不能被解释器解释为一个包,因此出错

解决问题1.3:
将主函数入口模块(顶级模块)放到相对导入时所涉及的包的外层包,即此处相对导入所涉及的包为sub_1_my_test,而外层包为my_test以及更上层的包。

具体操作如下:
将main_0_0.py放到my_test包下,修改后的包和模块的结构如下图所示:
在这里插入图片描述

# mul_div.py
class MuLDiv:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def mul(self):
        return self.a*self.b

    def div(self):
        return self.a/self.b
# 为了测试解决错误1.1和错误1.2,此处将main_0.py代码修改成
from . import mul_div
from .mul_div import MuLDiv


mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())

mul_div = MuLDiv(6, 2)
print(mul_div.mul())
print(mul_div.div())
# 将main_0_0.py修改成
from sub_1_my_test import main_0

以main_0_0.py作为主函数入口,运行main_0_0.py

"""
运行结果:
6
0.6666666666666666
12
3.0

Process finished with exit code 0
"""
2.2.2.2 相对导入错误2

错误2:

  • ValueError: attempted relative import beyond top-level package

错误示例:
所用包与模块的结构如下图所示:
在这里插入图片描述

# mul_div.py
class MuLDiv:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def mul(self):
        return self.a*self.b

    def div(self):
        return self.a/self.b
# add_sub.py
class AddSub:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a+self.b

    def sub(self):
        return self.a-self.b
# main_0.py
from . import mul_div
from .. import add_sub

mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
add_sub = add_sub.AddSub(2, 3)
print(add_sub.add())
print(add_sub.sub())
# main_1.py
from sub_1_my_test import main_0

以main_1.py作为主函数入口,运行main_1.py

"""
运行结果:
Traceback (most recent call last):
  File "E:/Project/PyCharm/test/relative_import_test/my_test/main_1.py", line 1, in <module>
    from sub_1_my_test import main_0
  File "E:\Project\PyCharm\test\relative_import_test\my_test\sub_1_my_test\main_0.py", line 2, in <module>
    from .. import add_sub
ValueError: attempted relative import beyond top-level package

Process finished with exit code 1
"""

问题分析错误2:
错误2的本质与错误1.3相同,都是忽略了相对导入时一个文件夹成为包的一个条件–该文件夹下不能含有顶层模块(含有主函数的模块)。此处是因为“..”导入时需要将my_test看作一个包,但由于主函数的入口main_1.py在my_test下,导致my_test不能被解释器解释为一个包,因此出错

解决问题2:
将主函数入口模块(顶级模块)放到相对导入时所涉及的包的外层包,即此处相对导入所涉及的包为sub_1_my_test、my_test,而外层包为relative_import_test以及更上层的包。

具体操作如下:
将main_1.py放到relative_import_test包下,修改后的包和模块的结构如下图所示:
在这里插入图片描述

# 只需要将main_1.py修改成以下代码,其它模块代码不变
from my_test.sub_1_my_test import main_0

以main_1.py作为主函数入口,运行main_1.py

"""
运行结果:
6
0.6666666666666666
5
-1

Process finished with exit code 0
"""
2.3 总结
2.3.1 以上问题总结

区别绝对导入与相对导入的关键点:

  • 是否使用了“.”、“. .”、“…”这种点的形式进行导入操作。

绝对导入:

  • 包所满足的条件:文件夹下只要有__init__.py。
  • 导入时所导入模块需要在sys.path的默认路径中按一定顺序进行搜索。

相对导入:

  • 包所满足的条件:文件夹下不但有__init__.py;且在该文件夹下不能出现主函数入口文件(顶级模块),原因是在相对导入中,主函数入口文件(顶级模块)导致其所在文件夹不会被解释器解释为包。
  • 对于含有相对导入的模块不能作为主函数入口文件(顶级模块),否则会报错。
2.3.2 建议项目编写规范

为了在项目中遇到以上导包问题,此处建议在实施时尽可能参照规范建立项目:

项目名
	| -> 包名称(同项目名)
			| -> __init__.py
			| -> 内部包1
				| -> __init__.py
				| -> A.py
				| -> B.py
			| -> 内部包2
				| -> __init__.py
				| -> C.py
				| -> 内部包3
					| -> __init__.py
					| -> D.py
	| -> 测试文件夹
			| -> test.py
	| -> 入口运行文件(main.py)

如图中的项目结构,规范如下:

  • 运行代码即通过main.py执行,而且不是以模块的形式(python -m 包名.main)。如果使用模块运行的方式,需在sys.path中添加项目所在目录或切到该目录执行。
  • 所有py文件都是用绝对引入的方式,如下示。这样的话对每个模块的单独测试放入测试文件夹,而不是在模块下直接运行(一般直接运行会报错,毕竟找不到path)。
## 以A.py为例
from 项目名包.内部包2.内部包3 import D
D.func()

3. 有关导入的一些其它知识点

3.1导入包和模块时__init__.py文件和被导模块的执行过程

在"from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package的__init__.py文件。如果都是package,则调用顺序是YY,XX。

示例:
所用包与模块的结构如下图所示:
在这里插入图片描述
#pic_center)

# my_test\sub_0_my_test\__init__.py
print(r"导入包_模块时__init__文件和被导模块的执行过程\my_test\sub_0_my_test\__init__.py")
# hello_0.py
print("hello_0.py")
# my_test\__init.py
print(r"导入包_模块时__init__文件和被导模块的执行过程\my_test\__init__.py")
# main.py
from my_test.sub_0_my_test import hello_0

以main.py作为主函数入口,运行main.py

"""
运行结果:
导入包_模块时__init__文件和被导模块的执行过程\my_test\__init__.py
导入包_模块时__init__文件和被导模块的执行过程\my_test\sub_0_my_test\__init__.py
hello_0.py

Process finished with exit code 0
"""
3.2 不同的导入方式

import语句:

  • import 包名
  • import 模块名

from…import…语句:

  • from 包名 import 包名
  • from 包名 import 模块名
  • from 模块名 import 类名/函数名/变量名
  • from 包名 import *
  • from 模块名 import *

总结
对于import…来说

  • import 后只能跟包名和模块名

对于from…import…来说

  • from后只能跟包名、模块名,除此之外from后还可结合“.”、“..”点的形式使用相对导入。
  • import后可跟包名、模块名、类名、函数名、变量名和*。
3.3 如何对导入的包、模块、类、函数、变量进行使用

假设a,b为包名,c为模块名,c中有个类C

import语句

import a.b.c

# 实例化一个C实例对象
test = a.b.c.C() # 不能写为test = C()

from…import…语句

from a.b.c import C

# 实例化一个C实例对象
test = C()
from a.b import c

# 实例化一个C实例对象
test = c.C()
from a import b

# 实例化一个C实例对象
test = b.c.C()
3.4 包名与模块名
3.4.1 模块名不能与其所在的包名同名,否则会报错。具体原因目前不知。

两种解决方案。
(1)重命名。
(2)

import sys
_cpath_ = sys.path[0] #获取当前路径
sys.path.remove(_cpath_) #删除
from jira import JIRA
sys.path.insert(0, _cpath_) #恢复
sys.path.remove(_cpath_) #删除 这一条可以将path中的第一条删除
sys.path.insert(0, _cpath_) #恢复这一条可以将path恢复成原来的样子

原理
程序总是将上级目录内容定位最高优先级,只要将它暂时屏蔽掉就可以了

3.4.2 包名模块名不能含有特殊字符

比如:"-" / "(" / ")"

4. import与__import__

import 和“__import__”都是用来导入module的,但是二者还是有所不同, 可以查看帮助文档来了解其不同.
先通过 help(“import”) 查看其帮助,可以找到如下的说明:

The basic import statement (no “from” clause) is executed in two
steps:

  1. find a module, loading and initializing it if necessary
  2. define a name or names in the local namespace for the scope where the “import” statement occurs.

上面描述了import 做的两步工作,首先是 查找一个模块或包,然后初始化这个模块或包,我个人对这一步的理解是这样的:

  • (1) 根据sys.path的值,在相应的目录下查找以name 命名的.pyc/.py 文件或者目录,如果找到的是.py/.pyc 文件(模块文件), 那么就执行这个.py/.pyc 文件,从而完成导入;如果找到的是以 name 命名的目录,这个目录里面通常包含一个 __init__.py / __init__.pyc 的文件, 就是调用这个文件来完成模块导入以及初始化的(比如 pkg_resources 模块).
  • (2) import 除了载入模块并完成初始化,还做了step 2 的工作:在import 发生的scope, 定义相应的namespace.

再通过help(import) 查看其帮助,我们可以看到如下的片段:

import(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python
interpreter and not for general use it is better to use
importlib.import_module() to programmatically import a module.

这里可以看到 import 是一个方法,这是__builtins__ 模块内置的一个方法,返回值是一个module,所以可以通过 import(ModuleName).Method的方式来使用,比如在一些项目中会看到__import(“pkg_resources”)__.run_script() 这样的方式来使用这种导入

既然两者都可以导入module, 那么使用上怎么选择呢?
import 导入模块之后,就在相应的namespace 中存在了,除非明确用del 从当前的namespace中移出,否则始终可以访问到;
而用__import__ 导入,返回的是一个 模块,可以引用这个模块,但是这个模块并不会“驻留”到相应的namespace. 也就是常用于 动态加载模块,从而可以根据需要 动态使用,节省系统资源;比如某个python 程序需要引用20个模块,但是这20个模块都只是引用其中某一个方法,那么完全没有必要 让这20个模块一直驻留在相应的namespace, 从而节省系统资源;




以上内容部分摘自以下参考博客
[参考博客及python手册]
ModuleNotFoundError: No module named ‘main.xxx’; ‘main’ is not a package问题
Python踩坑之路
Python中import的用法
python3.6.1手册–6.4. 包
Python包的相对导入时出现错误的解决方法
理解python的import与__import__
python文件名与包名冲突

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python-turtle模块Python语言的一个标准库,它是一个基于Tkinter的Python图形库,可以用来实现海龟绘图功能。Python-turtle模块使用海龟绘图语言来绘制图形,这种语言是由Seymour Papert在上世纪80年代发明的,旨在帮助儿童学习编程。Python-turtle模块提供了一种交互式的方式来绘制图形,比较适合初学者学习。 Python-turtle模块通过创建一个画布和一个海龟对象来实现图形绘制。画布是用来显示绘制的图形的窗口,海龟对象则是用来绘制图形的工具。Python-turtle模块中提供了许多绘图函数和方法,例如`forward()`、`backward()`、`right()`、`left()`、`circle()`等,可以用来绘制线条、形状、图案等。 Python-turtle模块不仅可以进行基本的绘图,还提供了一些高级绘图功能。例如,可以使用`t.begin_fill()`和`t.end_fill()`方法来填充图形,使用`t.penup()`和`t.pendown()`方法来控制画笔的起始和结束位置,使用`t.dot()`方法来绘制点等。 总的来说,Python-turtle模块是一个非常有用的工具,它可以帮助初学者了解图形绘制的基础知识,也可以用来进行简单的图像处理。在使用Python-turtle模块时,需要注意以下几点: 1. Python-turtle模块不是Python的内置模块,需要单独安装。 2. 在使用Python-turtle模块时,需要导入turtle模块。 3. 在创建海龟对象之前,需要先创建一个画布。 4. Python-turtle模块的绘图函数和方法非常多,需要仔细学习和理解。 5. 在进行图形绘制时,需要注意海龟对象的位置和方向。 综上所述,Python-turtle模块是一个非常有用的工具,可以帮助初学者学习图形绘制和编程基础知识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值