python 的模块与包

一 模块

1、什么是模块

模块是一系列功能的结合体

​ 分为三大类:

​ 1、内置的模块

​ 2、第三方的模块

​ 3、自定义的模块

​ 一个 python 文件本身就是一个模块,文件名:m.py 模块名:m

ps:模块分为四种形式

​ 1 使用python编写的.py文件

  2 已被编译为共享库或DLL的C或C++扩展

  3 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)

  4 使用C编写并链接到python解释器的内置模块

2、为何要用模块

1、内置与第三方的模块拿来就用,无需定义,这种拿来主义,可以极大地提升自己的开发效率

2、自定义模块,可以将程序的各部分功能提取出来放到一模块中为大家共享使用

​ 好处:减少代码冗余,程序组织结构更加清晰

3、如何使用模块

1 导入模块

image-20200327111216285

首次导入模块会发生的事情

import foo

1、产生 foo.py 的名称空间, 将 foo.py运行过程中产生的名字都丢到 foo 的名称空间中

2、执行 foo.py

3、在当前文件中产生的有一个名字 foo,该名字指向 1 中产生的名称空间

具体如下图:

image-20200326122226580

之后的导入,都是直接引用首次导入产生的 foo.py名称空间,不会重复执行代码

import foo
import foo
import foo

2 引用

具体:

print(foo.x)
print(foo.get)
print(foo.charge)

强调1:模块名.名字,是指名道姓地问某一个模块要名字对应的值,不会与当前名称空间中的名字发生冲突

x=1111111111111
print(x)
print(foo.x)

强调 2:无论是查看还是修改,操作的都是模块本身,与调用位置无关

import foo

x=3333333333
# foo.get()

foo.change()
print(x)

可以以逗号为分隔符在一行导入多个模块

import time, foo, m

3、导入模块的规范(顺序)

​ 1、内置模块

​ 2、第三方模块

​ 3、程序员自定义模块

模块是第一类对象

imoport foo

自定义模块的命名应该采用纯小写 + 下划线的风格

可以在函数内导入模块

def func():
    import foo

4、 py 文件的用途

​ 一个 py 文件有两种用途

​ 1)被当做程序运行

​ 2)被当做模块导入

1、执行 py 文件与导入文件的区别是什么?

​ 执行 py 文件:

​ 1)在内存中产生名称空间

​ 2)运行 py 文件

​ 3)将运行 py 文件产生的名字放到名称空间内

​ 名称空间会在文件运行结束后回收

​ 导入文件:

​ 1)产生模块文件的内存空间

​ 2)运行模块

​ 3)在该文件名称空间内产生模块字并指向模块文件的名称空间

2、from...import.....

​ 1、产生一个模块的名称空间

​ 2、运行 foo.py 将运行过程中产生的名字都丢到模块的名称空间去

​ 3、在当前名称空间拿到一个名字,该名字指向模块名称空间 中的某一个内存地址

image-20200327210340523
from foo import x #x = 模块foo中的值1的内存地址
from foo improt change
from foo import get
x = 111 #重新将 x 赋值 指向新的内存地址

​ 优点:代码更精简,导入后在使用时不需要加前缀

​ 缺点: 容易与当前名称空间混淆

一行导入多个名字(不推荐)

from foo import x,get,change

导入模块中所有名字(不推荐)

from foo import *

了解:

__ all __:在模块中默认存的是模块中所有名字,所以在其它地方能通过 from foo import * 导入所有的名字

__ all__ =['login', 'get'] #控制*代表的名字有哪些

起别名

from foo improt x as xx

3、循坏导入问题

循环导入问题指的是在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来调用第一个模块中名字,由于第一个模块尚未加载完毕,所以引用失败,抛出异常。原因是:在 python 中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会重复执行内部代码。

具体案列:

m1.py

print('正在导入m1')
from m2 import y

x = 'm1'

m2.py

print('正在导入m2')
from m1 import x

y = 'm2'

run.py

import m1

#1、执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
    import m1
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x'

#2、分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错

解决方案

方案一:

​ 将导入模块放到最后,保证在导入时,所有名字都已经加载过

# 文件:m1.py
print('正在导入m1')

x='m1'

from m2 import y

# 文件:m2.py
print('正在导入m2')
y='m2'

from m1 import x

# 文件:run.py内容如下,执行该文件,可以正常使用
import m1
print(m1.x)
print(m1.y)

方案二:

导入语句放到函数中,只有调用函数才会执行其内部代码

# 文件:m1.py
print('正在导入m1')

def f1():
    from m2 import y
    print(x,y)

x = 'm1'

# 文件:m2.py
print('正在导入m2')

def f2():
    from m1 import x
    print(x,y)

y = 'm2'

# 文件:run.py内容如下,执行该文件,可以正常使用
import m1

m1.f1()

注:循坏导入问题大多数情况下是因为程序设计失误导致的,在编写程序时应该尽量避免出现循环/嵌套导入。如果多个模块都需要共享某些数据,可以将共享的数据集中存放到某一个地方。

4、搜索模块的路径与优先级

​ 无论import 还是 from...impor在导入模块时都涉及到查找问题

优先级:

​ 1、内存(内置模块)

​ 2、硬盘:按照 sys.path中存放的文件的顺序依次查找要导入的模块

# mmm.py   #它是代码文件夹下的py 文件
import sys
print(sys.path) 
 
#环境变量路径
['/Users/tophan/Desktop/代码', '/Users/tophan/Desktop/代码', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/tophan/Library/Python/3.6/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']

1、列表中的每一个元素都可以看做一个文件目录
2、列表中第一个目录为执行文件的路径
3、第二个目录为整个项目的路径


当第一次导入模块时,如果被导入模块与执行文件在同一目录下是肯定可以正常导入的,如果不在同一目录下,就涉遍历查找该模块的存放目录是否在环境变量路径中。为了确保模块对应的源文件在任何地方都可以被找到,需要将源文件所在的路径添加到 sys.path中。

#sys.append(模块源文件的路径)

sys.path.append(r'/pythoner/projects/') #也可以使用sys.path.insert(……)

import foo #无论foo.py在何处,都可以导入它

5、区分py文件

区分一个 py 文件是执行文件还是导入文件

print(__name__)

1、当 py 文件被运行时,__name__的值为'__mian__'
2、当 py 文件被当做模块导入时,__name__的值为模块名

if __ name__ == '__main__':
  	print('文件被执行')
    get()
    change()
else:
  	#被当做模块导入时做的事情
    print('文件被导入')
    

二 包

1、包就是一个包含有__ init __ .py文件的文件夹

​ 包的本质是模块的一种形式,包是用来被当做模块导入,导入包其实其实是导入包下的__ init __文件

  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

  2. 创建包的目的不是为了运行,而是被导入使用,包只是模块的一种形式而已,包的本质就是一种模块

导入包会发生的事情:

1、产生一个名称空间

2、运行包下的__ init __.py 文件,将运行过程中产生的名字都 丢到 1(模块)产生的名称空间中

3、在当前执行文件的名称空间中拿到一个名字,这个名字指向 1 (模块)的名称空间

import mmm
print(mmm.x)
print(mmm.y)
mmm.say()

环境变量是以执行文件为准的,所有被导入的模块或者说后续的其他文件引用的 sys.path都是参照执行文件的 sys.path

也就是说,一个项目在执行文件运行时,项目中其他的文件的模块或包被导入和引用时都是去执行文件的 sys.path查找的,被导入模块中有 sys.path.append('路径'),在执行文件运行时,被导入的模块会将路径添加到环境变量列表中。

#模块:mmm
print('运行了。。。。')
import sys
sys.path.append('/11111111111')


#执行文件
import sys

import mmm
sys.path.append('/aaaaaaaaaaaaaaaaaaaaaaaaaa')
print(sys.path)

结果展示:
运行了。。。。
['/Users/tophan/Desktop/代码', '/Users/tophan/Desktop/代码', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/tophan/Library/Python/3.6/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend', '/11111111111', '/aaaaaaaaaaaaaaaaaaaaaaaaaa']

#被导入模块中有 sys.path.append('路径'),在执行文件运行时,被导入的模块会将路径添加到环境变量列表中。


2 导入包 和 模块遵循原则

1、凡是在导入时带点的,点的左边都必须是一个包,否则非法。

2、可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

from a.b.c.d.e.f import xxx
import a.b.c.d.e.f
#其中a、b、c、d、e 都必须是包

导入包:

1、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间

2、import 导入文件时,产生名称空间中的名字来源文件

​ import 包,产生的名称空间的名字同样来源于文件,即包下的__ init __ .py , 导入包本质就是导入该文件。

3、导入包的两种方式

1、绝对导入

​ 绝对导入,以包的文件夹作为起始来进行导入

import sys

from foo.m3 import f3

2、相对导入

​ 相对导入:仅限于包内使用,不能跨出包(包内模块之间的导入,推荐使用相对导入)

. :代码当前文件夹

.. : 代表上一层文件夹

#目录
foo--|
		bbb
     |
		m1
     |
    m2
		 |
    foo1
    
# 在foo1执行文件中
from .m1 import f1
from .m2 import f2
from .bbb.m4 import f4

强调:

​ 1、相对导入不能跨出包,所以相对导入仅限于包内模板彼此之间应用

​ 2、绝对导入时没有任何限制的,所以绝对导入是一种通用的导入方式

补充:

 在pycharm中导入自己写的模块时,得不到智能提示,并在模块名下出现下红线,但是代码可以执行,错误提示为下图所示:

image-20200327235803829

原因:出现 以上情况,是因为文件目录设置的问题,pycharm中的最上层文件夹是项目文件夹,在项目中导包默认是从这个目录下寻找,当在其中再次建立目录,目录内的py文件如果要导入当前目录内的其他文件,单纯的使用import导入,是得不到智能提示的,这是pycharm设置的问题,并非导入错误。

解决方法:

​ 从根目录下导入时,就会有提示信息

from ATM.lib.common import logger

原文地址:https://www.cnblogs.com/xy-han/p/12577463.html