Python之模块

Python之模块

一 模块介绍

1 什么是模块

1.1 模块的定义:

- 模块就是一个功能的集合体,不是用来直接运行的,而是用于被导入使用的。

1.2 模块的三个来源:

(1) python内置模块;
(2) 第三方模块;
(3) 自定义模块。

1.3 模块的四个类型:

(1) 一个py文件就是一个模块;
(2) 一个文件夹(包) 也是一个模块;
(3) 已被编译为共享库或DLL的DLL的C或C++扩展:
(4) 使用C语言编写并链接到python解释器的内置模块。

2 为何使用模块

### 2.1 使用别人的模块:
​		拿来主义,提升开发效率。

### 2.2 使用自定义模块:
​		为了减少代码冗余,方便程序的组织、管理。

二 模块的使用

1 模块使用之import语句

1.1 import的使用

# 以spam.py为例
print('from the spam.py')

money=1000

def read1():
    print('spam模块:',money)

def read2():
    print('spam模块')
    read1()

def change():
    global money
    money=0
# test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py'(spam.py文件中的可执行语句),当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam

'''
执行结果:
from the spam.py
'''

1.2 首次导入模块发生了三件事

		若重复导入会直接引用内存中已加载好的结果。

(1) 为源文件(如:spam模块) 创建新的名称空间,在spam中定义的函数和方法都在这个名称空间中,如果发生全局调用,则使用的数据也是该名称空间中的。

(2) 在新创建的名称空间的同时执行模块中包含的代码,即在import spam 代码写完的同时触发运行。

(3) 在当前名称空间中创建名字spam来引用该名称空间中的内容。

1.3 被导入的模块有独立的名称空间

​ 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个名称空间当作全局名称空间,这样就不会让被导入的模块与使用者的全局变量冲突。

(1) 测试一:money与spam.money不冲突

# test.py
import spam 
money=10
print(spam.money)

'''
执行结果:
from the spam.py
1000
'''

(2) 测试二:read1与spam.read1不冲突

# test.py
import spam
def read1():
    print('========')
spam.read1()

'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

(3) 测试三:执行spam.change()操作的全局变量money仍然是spam中的

# test.py
import spam
money=1
spam.change()
print(money)

'''
执行结果:
from the spam.py
1
'''

1.4 为模块起别名

​ 可以为导入的模块起别名,简化调用的复杂程度。

import spam as sm
print(sm.money)

​ 有两种sql模块mysql和Oracle,根据用户的输入,选择不同的sql功能

#mysql.py
def sqlparse():
    print('from mysql sqlparse')
#oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse()

1.3 可以一行导入多个模块

import sys,os,re

2 模块使用之from-import 语句

2.1 from...import...的使用

from spam import read1, read2

2.2 from...import 与import的对比

# 不同:
	-使用from...import...语句,是将spam中的名字直接导入当前名称空间,使用名字的时候不用加spam.进行调用了。

# 优势:
	-使用起来更方便。

# 劣势:
	-容易与当前执行文件中的名字冲突,相同名字的数据有覆盖效果。

(1) 验证一:当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=1000
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

(2) 验证二:如果当前有重名read1或者read2,那么会有覆盖效果。

#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1()
'''
执行结果:
from the spam.py
==========
'''

(3) 验证三:导入的方法在执行时,始终是以源文件为准的

from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000

'''
from the spam.py
100
spam->read1->money 1000
'''

2.3 支持as为模块起别名

from spam import read1 as read

2.4 支持一行导入多个名字

from spam import read1,read2,money

2.5 from...import * 的是使用

# from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置

# 大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)

'''
执行结果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''
2.5.1 __all__控制*导入的字段
# 可以使用 __all__ 来控制*(用来发布新版本) ,在spam.py中新增一行:
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

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
#测试一
执行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'

#测试一结果分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
#测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1
直接执行m1.py抛出异常
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):
  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
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
    from m2 import y
ImportError: cannot import name 'y'


#测试二分析
执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错
# 解决方法:
方法一:导入语句放到最后
#m1.py
print('正在导入m1')

x='m1'

from m2 import y

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

from m1 import x

方法二:导入语句放到函数中
#m1.py
print('正在导入m1')

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

x = 'm1'

# f1()

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

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

y = 'm2'

#run.py
import m1

m1.f1()

4 (了解)模块的重载

	- 考虑到性能的原因,每个模块只被导入一次,放入字典sys.module中,如果改变了模块的内容,则必须重启程序以重新加载模块,python不支持重新加载或卸载之前导入的模块。

5 py文件的两种用途

5.1 编写一个python文件可以有两种用途:

1.脚本:一个文件就是整个程序,用来被执行;

2.模块:文件中存放着一堆功能,也要来被导入使用。

5.2 python内置的全局变量__name__ :

### 1.用法(脚本与程序) :
​		当文件被当作脚本执行时: __name__ 等于 '__main__';

​		当文件被当作模块导入时: __name__ 等于模块名;

### 2.作用:
​		用来控制.py文件在不同的应用场景下执行不同的逻辑。
if __name__ == '__main__':  # 当作脚本运行时,__name__ 等于'__main__',则执行子代码进行测试;当作为模块导入的时候,条件为假,后续子代码块不执行。
    ...测试用的子代码

6 模块的搜索路径及优先级

6.1 模块的搜索路径

1.在第一次导入某个模块的时候,解释器会先检查该模块是否已被加载到内存(当前执行文件的名称空间对应的内存地址) ,(PS:当前程序导入某模块后,将该模块文件删除,模块仍可被调用执行) ;

2.若内存中没找到,再从内置模块中寻找;

3.最后从sys.psth环境变量列表中存在的文件夹里面依次寻找。

6.2 官方文档解释

	当一个名为 spam 的模块被导入的时候,解释器首先寻找具有该名称的内置模块。如果没有找到,然后解释器从 sys.path 变量给出的目录列表里寻找名为 spam.py 的文件。sys.path 初始有这些目录地址:

1) 包含输入脚本的目录(或者未指定文件时的当前目录) 。
2) PYTHONPATH (一个包含目录名称的列表,它和shell变量 PATH 有一样的语法) 。
3) 取决于安装的默认设置。

	在初始化后,Python程序可以更改 sys.path。包含正在运行脚本的文件目录被放在搜索路径的开头处, 在标准库路径之前。这意味着将加载此目录里的脚本,而不是标准库中的同名模块。 除非有意更换,否则这是错误。
posted @ 2020-03-16 21:18  越关山  阅读(340)  评论(0)    收藏  举报