模块

模块的介绍

什么是模块?

模块就是一系列功能的集合体,模块分为三大类,分别是内置的模块、第三方模块、自定义模块。

所谓内置模块就是Python解释器自带的模块,我们直接拿来用即可。

第三方模块则是别人写好的Python文件,上传到Python模块网站上我们通过pip或者手动下载的方式,下载到的py文件。

而自定义模块,则是我们自己写的模块即py文件。

一个Python文件本身就是一个Python文件,比如func.py,那么该文件的模块名就为func

模块分为四种形式:

1、使用Python编写的.py文件

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

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

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

其中2和4我们只做了解,和我们有关系的还是1和3。

为什么要用模块

1、内置模块与第三方模块拿来就用,无需自己编写或定义,这种拿来主义,可以大大提升我们的开发效率。而不low。

2、自定义模块可将程序的某些功能提取出来放到一个模块中,可以供大家使用,第三方模块就是这么来的。好处是减少代码的冗余,程序的整体架构较清晰,

如何导入模块

import time # 导入内置模块time

模块最大的作用就是,将一个Python文件的功能,引入到另外一个Python文件中去。

比如有如下的应用场景:

x1ong.py文件有如下代码:

print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')

你想让x1ong.py文件的功能,应用到test.py文件中去,如果没有模块的概念,那么你只能去将x1ong.py的代码复制给test.py去。

但是你有了调用模块之后,你只需要import一下即可。

模块插图

首次导入模块发生的三件事

import x1ong # 导入一个自定义模块。

'''
首次导入模块发生的三件事:
1. 执行x1ong.py文件
2. 产生x1ong.py的名称空间,将x1ong.py运行过程中产生的名字都丢到x1ong的名称空间中
3. 在当前py文件中有个名字为x1ong,该名字执行第二件事产生的名称空间中
'''
print(x1ong) # <module 'x1ong' from '/Users/x1ong/Downloads/x1ong.py'>

x1ong.py文件的内容如下:

print('from x1ong moudel >>>>')

x = 10
def func():
    print(x)

def change():
    global x
    x = 10000

import导入模块的基本使用

import x1ong
# 引用
 
print(x1ong.func)
print(x1ong.change)
print(x1ong.x)

'''
强调1: 模块名.名字,是指名道姓的问某一个模块要一个名字,不会与当前文件的名称空间发生任何冲突
 
'''

# 调用x1ong模块下的func函数
x1ong.func() # 10 

# 调用x1ong模块下的change函数
x1ong.change() # 更改全局变量x的值为10000

'''
强调2: 无论是查看还是修改,都是以原模块的Python文件为基准的,与调用位置无关
'''

x = 123
x1ong.func() # 再次打印x 为 10000,可以看到并没有修改x的值为123,可见强调2总结

import导入模块的相关知识

1、可以在一行导入多个模块,以逗号分割即可。

# import sys
# import time 
# import os 

# 上述代码可以写为如下形式
import sys,time,os  # 但是不推荐使用

2、导入模块的规范

  • 先导入内置模块
  • 再导入第三方模块
  • 最后再导入自定义模块
'''
import time
import sys
import os 


import 第三方模块1
import 第三方模块2
import 第三方模块3

import 自定义模块1
import 自定义模块2
'''

3、在import导入的时候为某个模块设置别名

import 模块名 as 别名

import time as t

# 如果为time设置了别名,则在以后调用time模块的时候。使用t.方法()调用即可
print(t.time()) 

# 设置了别名之后,原始的名称则不能再使用,不然会提示未定义。
# print(time.time()) #  name 'time' is not defined

这种别名功能,一般在用于对某个第三方模块或自定义模块的模块名称较长的时候,使用别名功能。

4、模块是第一类对象

import time 

x1ong = time # 模块名可以作为值赋值给某个变量


# 都可以调用time()函数
print(x1ong.time()) 
print(time.time())

5、Python3的时候,所有的自定义模块都应该以纯小写+下划线的命名风格规范,但是在python2的时候,有使用驼峰命名法的规范,但是到了Python3之后,都改用了纯小写+下划线的命名风格。

模块插图1

6、可以在函数内导入模块

def func():
	import time
	print(time.time())
func()
# time.time() # 报错

但是在函数内导入的模块,无法在函数外使用,这跟函数内的变量,无法在函数外使用一个道理。

如果不是功能的需要,建议在Python的文件开头导入模块。

模块和当前文件的名称空间

test.py文件的内容如下:

import x1ong

x = 1
y = 2
print('1111')
print('2222')

print(x1ong.x)
print(x1ong.get)
print(x1ong.change)

x1ong模块下的内容如下:

print('from x1ong moudel >>> ')

x = 1
def get():
    print(x)

def change():
    global x
    x = 0

他们的关系图为:

模块插图2

可以发现,模块x1ongtest.py文件中定义的变量,不再一个名称空间中,但是我们通过x1ong.get调用模块x1ong的内容时,则是使用引用的方式,引用到get的内存地址。

__name__这个属性在每一个Python文件中都有值,我们可以通过print(__name__)打印。结果是一个字符串类型

如果一个文件是被作为模块引用进来的,那么该文件的__name__属性返回的值就为模块的名称,即文件名称。

如果一个文件没有被作为模块引用,那么该文件的__name__属性返回的值为'__main__'

我们常常使用如下if语句,判断当前文件是否被作为模块引用:

if __name__ == '__main__':
    # 文件没有被引用执行的代码
    print('文件没有被引用')
else:
    # 文件被引用了执行的代码
    print('文件是被引用的')

from..import..用法

在讲from..import..之前,我们先总结一下import功能的优点与缺点:

优点:绝对不会与当前名称空间的名称冲突

缺点:要加上模块的名称即模块名.做前缀,比较麻烦。

from..import..可以从一个模块中,单独导入一个名称,并且不用加模块名.的前缀。

from x1ong import x
from x1ong import get
print(x)
print(get)

from..import..导入发生的三件事

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

2、运行x1ong.py的时候,将运行过程中产生的名字都存放到模块的名称空间中。

3、在当前名称空间拿到一个名字,该名字与名称空间中某一个内存地址做绑定。

from..import..的优缺点

优点:在调用模块下某个名称的时候,不需要加上前缀。即模块名.

缺点:模块中的名称容易与当前名称空间的名称混淆。

from..import..的其他知识

1、在一行导入多个名字,中间使用逗号进行间隔

# from x1ong import x 
# from x1ong import get
# from x1ong import change

from x1ong import x,get,change # 不太推荐使用 

如果在一个模块中,使用的名称稍微比较多,我们可以使用这种方法。

# *号: 可以导入一个模块中的所有名字

from x1ong import * # 但是不推荐使用,容易混淆。因为*导入的为模块中所有的名字,会与我们当前文件的名称冲突。

# 了解:__all__

# 其实*查找的就是模块文件中的__all__属性的值,该值是一个列表,存放的为当前文件的所有名称。
__all__ = ['x','change'] # 为模块x1ong添加右侧内容

之后我们再test.py导入模块x1ong

from x1ong import * 

get() # 报错,提示未定义,因为模块x1ong的__all__的列表中,没有名字get
print(x) # 正常 1
print(change) # 正常

循环导入问题

循环导入指的是在一个模块加载或导入中导入了另外一个模块,而在另外一个模块中,又返回来导入第一个模块中的某一个名字,由于第一个模块,未加载完毕,所以引出失败,异常等情况。原理就是在python中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码。

我们使用以下示例来演示循环导入的问题:

m1.py文件内容如下:

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

x = 10

m2.py文件内容如下:

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

y =  20

test.py文件内容如下:

import m1

运行test.py抛出的错误:

正在导入m1...
Traceback (most recent call last):
正在导入m2...
  File "/Users/x1ong/Downloads/test.py", line 1, in <module>
    import m1
  File "/Users/x1ong/Downloads/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/x1ong/Downloads/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x' from 'm1' (/Users/x1ong/Downloads/m1.py)
[Finished in 254ms with exit code 1]

# 分析:
'''
运行test.py之后————>import m1,开始导入m1并运行其他内部代码 ————> 打印内容 "正在导入m1..." 执行 from m2 import y
开始导入m2并运行其内部代码,之后打印"正在导入m2...",执行from m1 import x,由于m1已经被导入过了,不会再重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错。
'''

解决方案:

# 方案1:导入语句放到最后,保证所有名字都被加载

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

x = 10


from m2 import y

# 文件: m2.py

print('正在导入m2...')

y =  20

from m1 import x

# 此时再运行test.py,即可正常运行。


# 解决方案2:导入语句放到函数中,只有在调用函数时才会执行其内部代码

# 文件: m1.py

print('正在导入m1...')

x = 10

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

# 文件: m2.py

print('正在导入m2...')

y =  20

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

总结:循环导入问题基本上都是程序设计失误导致的,上述解决方案也非常的不推荐使用,尽量在实际开发中,规避这种循环导入的问题。如果多个模块需要共享一些名称,可以将共享的名称放到一个模块中,之后再进行导入。

搜索模块的路径与优先级

无论是import或者from..import..在导入一个模块的时候,如果该模块被加载到内存,则直接引用,否则就会在指定的位置去查找模块。之后按照从左到右的顺序去查找sys.path规定的路径。sys.path的返回值是一个列表。

x1ong@bogon downloads % python3 test.py 
['/Users/x1ong/Downloads', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']

列表的每一个值都可以被当做一个目录去看,Python解释器会在该列表中每个元素指定的路径去寻找模块。

sys.path的第一个值,为我们当前Python文件所在绝对路径。

sys.modules返回的值为一个字典类型,它里面定义了我们所有加载到内存的模块名称。

编写模块的规范

我们在编写py文件的时候,需要时刻注意,我们的Python文件是给自己用的,还是要当做模块,去给比人去用,如果其他人还要使用,那么代码的可读性和可维护性就显得格外的重要。

'''
The module is used to .......
'''

import os # 导入其他模块

y = 20 # 非必要不适用全局变量,尽量使用局部
class Foo: # 定义类,做好对类的注释
    '''
     Class Foo is used to...
    '''
    pass

def func: # 定义函数,做好对函数的注释
    '''
    function func is used to...
    ''
    pass

if __name__ == '__main__': # 主程序
    func() # 在当做脚本执行的时候,执行这里面的代码。
本文作者: x1ong
免责声明:本博客所有文章仅用于学习交流
转载声明:文章为作者原创文章 转载请注明来源
本文链接: https://www.giaoblog.com/python/23182.html
上一篇
下一篇