python的模块的导入和包的导入

1—Python学习 专栏收录该内容
58 篇文章 5 订阅

python导入视频教程

知乎专栏除了python教程,还有很多其他教程:

在这里插入图片描述


1 模块(module)

1.1 模块的定义

模块:用来从逻辑上组织代码(变量、函数、类、逻辑:实现一个功能),本质就是.py结尾的python文件(文件名:test.py,对应的模块名:test)

包:包用来从逻辑上组织模块的,本质就是一个目录(必须带有一个__init__.py文件)

  • 在python中,一个py文件就是一个模块
  • 文件名是module.py模块名是module
  • 导入模块,可以引用模块中已经写好的功能

如果把开发程序比喻成制造一台电脑,编写模块就像是在制造电脑的零部件,准备好零部件后,剩下的工作就是按照逻辑把它们组装到一起。

2 模块导入方法

2.1 import 语句和from-import语句

2.1.1 import 语句

1、直接导入模块名

import module_name

2、同时导入多个模块

同时导入多个模块有两种写法:

  • 1)import导入多个模块,可以写成多行import语句(推荐
import module1
import module2
import module3
...
import moduleN
  • 2)还可以在一行导入,用逗号分开不同 模块(不推荐

import module1, module2, module3, ..., moduleN

推荐使用第一种导入多个模块,更规范、可读性更强

2.1.2 from-import语句

1、把模块下所有的方法和变量都导入(不推荐

from module_alex import *

2、指定导入模块下的需要用到的方法属性等(推荐

from module_alex import m1, m2, m3

2.1.3 其他导入语句as

模块导入的时候还可以通过as语句起个别名

from module_alex import logger as logger_shl

通过定义别名,也可以防止在当前定义的函数logger名字相同而覆盖我们导入的logger。或者直接使用import module_alex; module_alex.logger这种写法导入,也是可以避免重名带来的覆盖影响!

2.2 自定义同级目录的模块导入

下面我们想要在main.py文件中应用foo.py模块中的功能,需要使用import foo

2.2.1 使用import导入实例

1、文件目录结构

(base) shl@zhihui-mint:~/project$ tree
.
├── foo.py   # 自定义的模块module
└── main.py  # 在main.py文件中导入引用foo模块

2、自定义模块foo.py文件中定义的内容


x = 1

def get():
    print(x)


def change():
    global x
    x = 0


class Foo:
    def func(self):
        print('from the func')

3、在main.py文件中中引用foo.py模块中的功能,main.py文件中定义的内容

import foo #导入模块foo
a=foo.x #引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get() #调用模块foo的get函数
foo.change() #调用模块foo中的change函数
obj=foo.Foo() #使用模块foo的类Foo来实例化,进一步可以执行obj.func()

import foo可以理解成把foo.py文件中所有的代码粘贴到main.py文件中,然后解释一遍!

2.2.2 使用from-import导入实例

1、from moudle_name import func1, func2,...

还是上面的例子,from…import…与import语句基本一致,唯一不同的是:使用import foo导入模块后,引用模块中的名字都需要加上foo.作为前缀,而使用from foo import x,get,change,Foo则可以在当前执行文件中直接引用模块foo中的名字,如下

from foo import x,get,change #将模块foo中的x和get导入到当前名称空间
a=x #直接使用模块foo中的x赋值给a
get() #直接执行foo中的get函数
change() #即便是当前有重名的x,修改的仍然是源文件中的x

注意:

无需加前缀的好处是使得我们的代码更加简洁,坏处则是容易与当前名称空间中的名字冲突,如果当前名称空间存在相同的名字,则后定义的名字会覆盖之前定义的名字。

2、from foo import *:代表将foo中所有的名字都导入到当前位置

from foo import * #把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字

a=x
get()
change()
obj=Foo()

3、上面通过*导入的所有的功能,模块编写者可以通过定义__all__变量来控制*代表的意思:

如下,把foo.py文件中内容修改为:

#foo.py

__all__ = ['x', 'get']
x = 1

def get():
    print(x)
def change():
    global x
    x = 0

class Foo:
    def func(self):
        print('from the func')

此时在main.py中通过*导入:

from foo import * #此时的*只代表x和get

x #可用
get() #可用
change() #不可用
Foo() #不可用

3 import导入的本质(路径搜索和搜索路径)

导入模块的本质就是把python文件解释一遍

import module_name ----> module_name.py----->module_name.py的路径 导入module_name就是要找到一个module_name.py的文件,就是找到是module_name.py文件的路径

3.1 python模块导入路径搜索规则

1、使用sys.pathpython的搜索模块路径集,是一个list,如下是我的python搜索的路径集:

(base) shl@zhihui-mint:~$ python
Python 3.8.3 (default, Jul  2 2020, 16:21:59) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', 
'/opt/ros/melodic/lib/python2.7/dist-packages', 
'/home/shl/anaconda3/lib/python38.zip', 
'/home/shl/anaconda3/lib/python3.8', 
'/home/shl/anaconda3/lib/python3.8/lib-dynload', 
'/home/shl/anaconda3/lib/python3.8/site-packages', 
'/home/shl/anaconda3/lib/python3.8/site-packages/bar_chart_race-0.2.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/torch2trt-0.1.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/pycolmap-0.0.1-py3.8-linux-x86_64.egg']
>>> 

2、导入模块的时候,就是搜索py文件的过程,先从第一个''是空,表示在当前路径下搜索,如果没有在从第二个路径下搜索,以此类推,如果没有找到就报没有该模块的错误!

如果想要导入的模块,一定要保证sys.path的列表中包含要查找模块的路径

3、列表中的每个元素其实都可以当作一个目录来看:

  • 在列表中会发现有.zip或.egg结尾的文件,二者是不同形式的压缩文件,事实上Python确实支持从一个压缩文件中导入模块,我们也只需要把它们都当成目录去看即可。
  • sys.path中的第一个路径通常为,代表执行文件所在的路径
  • 为了确保某个py文件无论在何处都可以被导入,只要把该.py文件所在的目录添加到sys.path列表中即可!!!

3.2 搜索模块的路径与优先级

1、模块其实分为四个通用类别,分别是:

  • 1)使用纯Python代码编写的py文件

  • 2)包含一系列模块的包

  • 3)使用C编写并链接到Python解释器中的内置模块

  • 4)使用C或C++编译的扩展模块

2、模块导入的优先级

在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找模块对应的文件为止,否则抛出异常。sys.path也被称为模块的搜索路径,它是一个列表类型

3.3 导入不同级目录的文件

还是上面的两个文件,让main.py导入foo模块,但是文件的位置发生了变化,如下:

(base) shl@zhihui-mint:~$ pwd
/home/shl
(base) shl@zhihui-mint:~$ tree project
project
├── foo.py
└── src
    └── main.py

1 directory, 2 files
(base) shl@zhihui-mint:~$ 

我们的目的是在执行main.py文件的时候能够正确导入foo模块,因此只要在导入foo模块之前,把foo.py所在的目录路径添加到python的搜索模块路径集的列表中即可!详细过程见下面代码中的注释!

1、foo.py文件内容:

x = 1

def get():
    print(f"x value is: {x}")

def change():
    global x
    x = 0

class Foo:
    def func(self):
        print('from the func')

2、main.py文件内容:

import sys
import os

# sys.path
print(sys.path)
'''
['/home/shl/project/src', 
'/opt/ros/melodic/lib/python2.7/dist-packages', 
'/opt/ros/eloquent/lib/python3.6/site-packages', 
'/home/shl/anaconda3/lib/python38.zip', 
'/home/shl/anaconda3/lib/python3.8', 
'/home/shl/anaconda3/lib/python3.8/lib-dynload', 
'/home/shl/anaconda3/lib/python3.8/site-packages', 
'/home/shl/anaconda3/lib/python3.8/site-packages/bar_chart_race-0.2.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/torch2trt-0.1.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/pycolmap-0.0.1-py3.8-linux-x86_64.egg']

'''

# __file__ 是当前执行文件的文件文件名
print(f"file name: {__file__}")  # file name: main.py


# os.path.abspath(file)获取文件的绝对路径
file_abspath = os.path.abspath(__file__)
print(file_abspath)  # /home/shl/project/src/main.py


# os.path.dirname(path) # 获取指定路径的父级目录
dir_path = os.path.dirname(os.path.abspath(__file__))
print(dir_path)  # /home/shl/project/src


# 如果连续获取父级目录,只要调用两次os.path.dirname(os.path.dirname(path))即可!
dir_path2 = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(dir_path2)  # /home/shl/project


# 把我们要导入模块的py文件所在目录路径添加到sys.path列表中即可
# 当然我们只要添加dir_path2这个路径即可,上面只是为了清楚我们添加foo.py这个模块所在目录路径的过程!
sys.path.append(dir_path2)

# 再次查看foo.py所在的目录/home/shl/project已经添加到了sys.path列表中!
print(sys.path)
'''
['/home/shl/project/src',   # 当前main.py所在的路径
'/opt/ros/melodic/lib/python2.7/dist-packages', 
'/opt/ros/eloquent/lib/python3.6/site-packages', 
'/home/shl/anaconda3/lib/python38.zip', 
'/home/shl/anaconda3/lib/python3.8', 
'/home/shl/anaconda3/lib/python3.8/lib-dynload', 
'/home/shl/anaconda3/lib/python3.8/site-packages', 
'/home/shl/anaconda3/lib/python3.8/site-packages/bar_chart_race-0.2.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/torch2trt-0.1.0-py3.8.egg', 
'/home/shl/anaconda3/lib/python3.8/site-packages/pycolmap-0.0.1-py3.8-linux-x86_64.egg', 
'/home/shl/project']  # 我们通过sys.path.append(dir_path2) 添加的路径,
                    # 而我们要导入的模块foo就在/home/shl/project 这个目录下,因此此时可以导入foo模块

'''

import foo  # 导入模块foo

a = foo.x  # 引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get()  # 调用模块foo的get函数
foo.change()  # 调用模块foo中的change函数
obj = foo.Foo()  # 使用模块foo的类Foo来实例化,进一步可以执行obj.func()

3、此时执行就可以正确导入foo模块了,否则会报错:ModuleNotFoundError: No module named 'foo'

(base) shl@zhihui-mint:~/project/src$ python main.py 
Traceback (most recent call last):
  File "main.py", line 60, in <module>
    import foo  # 导入模块foo
ModuleNotFoundError: No module named 'foo'
(base) shl@zhihui-mint:~/project/src$ 

注意:

因为是从sys.path的列表中从左到右开始找的,如果比如此时其他路径中刚好也有一个和我们要导入的同名模块,此时就不会继续找了,因此解决方法就是用插入的方法。

把我们要添加的路径添加到列表的首位:

sys.path.insert(index=0, obj=dir) #把路径dir添加到sys.path列表中的索引0的位置

4 导入包

1、写一个项目会建一堆的目录结构,这个目录就是我们右键新建Pyhton Package(有一个__init__.py文件

2、:用来从逻辑上组织模块的,本质就是一个目录(与普通目录的区别,就是包必须带有一个__init__.py文件

3、导入包的本质:

  • 就是去解释这个包下的__init__.py文件
  • 或者说的更直接一点,导入包的本质就是去执行该包下的__init__.py文件

4、总结:

  • 导入模块就是相当于把xxx模块对应的xxx.py中的全部代码,在当前导入的位置解释一遍(可以更通俗的理解就是把xxx.py中的全部代码粘贴到我们导入的这个位置)

  • 导入包就是去执行包下的__init__.py文件

  • 导入xxx模块是解释xxx模块对应的xxx.py文件导入xxx包是运行的是xxx包下的__init__.py文件

4.1 导入包:就是执行包下的__init__.py文件

下面我们在main.py文件中导入包module_test,看一下结果

(base) shl@zhihui-mint:~$ pwd
/home/shl
(base) shl@zhihui-mint:~$ tree project
project
├── main.py
└── module_test
    └── __init__.py

1 directory, 2 files
(base) shl@zhihui-mint:~$ 

1、main.py文件中内容

import module_test

2、__init__.py文件中内容:

print("in the package")

3、执行python module_test.py文件

此时可以发现会在终端打印出:in the module

4.2 导入包下的模块

我们导入包的目的最终是要导入包下面的模块,下面我们在包module_test下再新建一个模块test.py,然后我们在main.py中导入该包下的test.py模块,文件目录结构如下:

(base) shl@zhihui-mint:~$ pwd
/home/shl
(base) shl@zhihui-mint:~$ tree project
project
├── main.py
└── module_test
    ├── __init__.py
    └── test.py

1 directory, 3 files
(base) shl@zhihui-mint:~$ 

4.2.1 测试导入包中的模块1

1、main.py文件内容:

import module_test

module_test.test.func()

2、test.py文件内容:

def func():
    print("in the test")

3、__init__.py文件内容:

print("in the package")

4、此时执行main.py文件,结果如下:

(base) shl@zhihui-mint:~/project$ python main.py 
in the package
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    module_test.test.func()
AttributeError: module 'module_test' has no attribute 'test'
(base) shl@zhihui-mint:~/project$ 

会发现找不到test模块,这是因为:

  • 导入包的本质是执行__init__.py文件,我们发现__init__.py文件中的print("in the package")是被正确执行了

  • 因此,我们需要在__init__.py文件中定义导入test.py文件的模块的

4.2.2 测试导入包中的模块2

1、只需要修改__init__.py文件内容为如下:

print("in the package")

# 相对导入,从当前目录下导入test模块
from . import test

# import test   # __init__.py和test.py是同级目录,因此__init__.py文件中肯定能够导入test.py模块
# test <- test.py all code ,import test就是把test.py中的所有代码加载到当前位置,然后赋值给test变量

注意:

按理说直接在__init__.py文件中直接导入import test就可以,但是egon在讲解的时候依旧是找不到test模块,我自己测试也是,因此还是推荐使用from . import test这种相对导入的方式!!!

2、此时再执行main.py文件,即可正确导入module_test包下的test.py模块,执行结果如下:

(base) shl@zhihui-mint:~/project$ python main.py 
in the package
in the test
(base) shl@zhihui-mint:~/project$ 

当然,你也可以在main.py文件中直接导入包下的test.py模块,不通过导入包的方式,如下main.py中直接导入包下的模块也是可以的:

#import module_test.test as test

#test.func()


from module_test.test import func

func()

5 导入优化

import module

module.test() 有100处用到了这个代码,就需要每次调用先去找module这个模块对应的module.py对应的路径,然后导入test()函数,效率就比较低

from module import test

这样就只有第一次需要找路径,后面就直接使用test()函数了!

6 模块的分类

  • 1)标准库(python解释器自带的内置模块,例如sys、os等)
  • 2)开源模块(或第三方模块,例如github上的一些python开源的模块)
  • 3)自定义模块
  • 1
    点赞
  • 1
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值