加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python中的模块和包

发布时间:2020-05-27 10:10:42 所属栏目:Python 来源:互联网
导读:模块 一 什么是模块 模块就是一组功能的集合体,可以通过导入模块来复用模块的功能。 比如我在同一个文件夹定义两个.py文件,分别命名为A.py和B.py,那么可以通过在A文件里通过 import B

<h1 id="模块">模块
<h3 id="一-什么是模块">一 什么是模块

模块就是一组功能的集合体,可以通过导入模块来复用模块的功能。

比如我在同一个文件夹定义两个.py文件,分别命名为A.py和B.py,那么可以通过在A文件里通过import B来使用B文件里的名称空间。

python中,模块的使用方式都是一样的,可以分为四个通用类别:

  1. 使用python编写的.py文件
  2. 已被编译为共享库或DLL的C或C++扩展
  3. 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
  4. 使用C编写并链接到python解释器的内置模块

  1. 从文件级别组织程序,便于管理

    随着需求的增多,功能也会越来越多,为了方便管理,通常将程序分成多个文件,这样项目的结构会更加清晰,方便管理。这时不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现功能复用。

  2. 使用别人写好的模块,提高开发效率

    使用别人已经写好的轮子,在自己的项目中使用,可以极大地提高开发效率。

    注意:当退出python解释器的时候,重新进入那么之前定义的函数和变量都会丢失,因此通常将程序写到文件中以便永久保存,需要时可以在命令行通过python *.py方法执行。

# test1.py
print('from test1')

money = 10
def func1():
print('test1模块:',money)

def func2():
print('test1模块')
func1()

def change():
global money
money = 100


<pre class="python"># test2.py
import test1
test1.func1()

<pre class="python"># 运行结果
from test1
test1模块: 10

当在tes2t导入的时候会执行test1中的代码,所以首先会打印from test1接着执行test1中的func1函数。

模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次导入时才会执行(import语句可以在程序中的任意位置使用的,且针对同一个模块可以import多次,python为了防止重复导入,当第一次导入模块时就将模块的名称空间加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,并不会重复执行模块内的语句)

# test3
print('from test3')
# test4
import test3
import test3
import test3
import test3
# 运行结果
from test3

ps:可以导入sys模块,调用sys.module查看当前加载到内存中的模块,sys.module是一个字典,内部包含模块名和模块名路径的对应关系。该字典决定了导入模块时是否需要重新导入。

运行py文件导入一个模块时,解释器做了三件事:

  1. 在执行文件中为被导入文件创建新的名称空间,在被导入模块中定义的函数和方法若是使用到了global时访问的就是这个名称空间。

  2. 在新创建的名称空间中执行模块中的包含的代码。函数定义也是‘’被执行‘’的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,可以用globals()可以查看。

  3. 在执行文件的名称空间创建被导入模块的名称来引用该名称空间。

    这个名字和变量名没什么区别,都是‘’第一类‘’的,且使用句点表示法可以访问被导入模块的名称空间,导入的模块与被导入的模块是独立的两个名称空间。

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

  • 测试1
# test5.py
import test1
money = 100
print(test1.money)
# 运行结果
from test1
10
  • 测试2
# test6
import test1
def func1():
    print('from test6 func1')
test1.func1()
func1()
# 运行结果
from test1
test1模块: 10
from test6 func1
# 这说明 test6中的函数和test1中的函数不冲突
  • 测试3
# test7
import test1
money = 1
test1.change()
print(money)
# 运行结果
from test1
test1模块: 10
1
# 这说明test1中的change函数只是修改了test1中的全局变量,对test7中的变量没有操作权限

区别就是:使用from...import…是将被导入模块中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了,不需要在前面加上模块名前缀。

from...import…导入方式的优缺点:

  • 好处:使用方便
  • 坏处:容易与当前执行文件的名字冲突

  1. 验证1:当前位置直接使用test1中的函数名,执行时仍然以test1.py文件为全局名称空间
# 导入的函数func1,执行时仍然回到test1.py中寻找全局变量money
# test8
from test1 import func1
money = 50
func1()
# 运行结果
from test1
test1模块: 10
# 导入的函数func2,执行时需要调用func1,仍然回到test1.py中找func1
#test9.py
from test1 import func2
def func1():
    print('-----')
func2()
# 运行结果
from test1
test1模块
test1模块: 10
  1. 验证2:如果当前名称空间和被导入的模块中的名字重合,那么会覆盖掉前面的名字
# 导入的函数fun1,被当前位置定义的func1覆盖掉了
# test10
from test1 import func1
def func1():
    print('======')
func1()
# 运行结果
from test1
======
# 这说明func1把被导入名字func1覆盖掉了
# 当前位置定义的func1导入func1
# test11
def func1():
    print('======')
from test1 import func1
func1()
# 运行结果
from test1
test1模块: 10
# 这说明在后面导入的话会覆盖掉前面的
  1. 验证3:被导入的方法执行时,始终以源文件为准
# test12
from test1 import money,func1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
func1() #读取spam.py中的名字money,仍然为1000
# 运行结果
from the test
100
test1模块: 10
# 可以看出运行func1的时候依然从原名称空间查找的

from ... import * 是把被导入文件中所有不是以下划线(_)开头的名字都导入到当前名称空间。

大部分情况下不应该以这种导入方式,因为不知道被导入包中的名字是否会和当前名称空间中的名字重合造成名字覆盖。

解决方法是在被导入文件中使用__all__ = []来控制被导入的名字,只有在__all__里面的才会被导入。

模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入。

# test1.py
print('正在导入1')
from test2 import y
x = '1'
# test2.py
print('正在导入2')
from test1 import x
y = '2'
# run.py
import test1
# 运行结果
正在导入1
正在导入2
Traceback (most recent call last):
  File "/Users/jingxing/PycharmProjects/python全栈/day18/pack/run.py",line 6,in 
    import test1
  File "/Users/jingxing/PycharmProjects/python全栈/day18/pack/test1.py",line 7,in 
    from test2 import y
  File "/Users/jingxing/PycharmProjects/python全栈/day18/pack/test2.py",line 8,in 
    from test1 import x
ImportError: cannot import name 'x'

分析:在run文件中执行导入test1,运行test1的代码,打印并且从test2中导入y,回到test2,打印并且从test1中导入,因为已经导入test1了(没导入完全,因为代码没执行完),所以直接找'x',但因为在test1中的代码执行不下去,所以报错。执行文件不等于就完全导入文件了。

解决方法1:导入语句放在最后

解决方法2:导入语句放在函数中(因为在导入模块时,函数内的代码并不会执行,只会判断语法错误,所以这时候导入模块可以完全导入)

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

就算在修改已经导入的模块里面的代码对运行结果也没影响。

  1. 脚本,一个文件就是整个程序,用来被执行
  2. 模块,文件中存放着一堆功能,用来被导入使用

python内置了全局变量__name__,

  • 当文件被当做脚本执行时:__name__等于'main'
  • 当文件被当做模块导入时:__name__等于模块名

作用:用来控制.py文件在不同的应用场景下执行不同的逻辑

if __name__ == '__main__':
    pass

模块的查找顺序是:内存中已经加载的模块--》内置模块--》sys.path路径中包含的模块

详细:在第一次导入某个模块式,会先检查该模块是否已经被加载到内存中(当前执行文件的名称空对应的内存),如果有则直接引用;

ps:python解释器会在启动时自动加载一些模块到内存中,可以使用sys.module查看。

如果在内存中没有,解释器会查找同名的内建模块;

如果还没有则去sys.path给出的目录列表中查找。

了解:sys.path的初始化的值来自于:

The directory containing the input script (or the current diretory whrn no files is specified).

PYTHONPATH (a list of directory names,with the same syntax as the shell variable PATH).

The installation-dependent default.

在初始化后,python程序可以修改sys.path,路径放在前面的优于标准库被加载。

搜索时按照sys.path中从左到右的顺序查找,位于前面的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理。

.egg文件是setuptools创建的包,这是按照第三方python库和扩展时使用的一种常见格式,.egg文件实际上只是添加了额外元数据(如版本号,依赖项等)的.zip文件。

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读