Python学习笔记5:模块/包

1.模块
模块简单理解就是一组功能的集合。
在Python中,一个文件(.py)就是一个模块,文件名即模块名。
模块的好处是大大提高代码的可维护性,其次,代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。

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

#!/usr/bin/python3
# -*- coding: utf-8 -*-

#定义名为learn.py的模块
print('beginning learn module')
modulename = 'learn'

def fun1():
    print('hello fun1')

def fun2():
    print('hello fun2')

print('end learn module')

#在另外一个文件中导入这个模块
import learn
import learn
#打印输出
beginning learn module
end learn module
#可以发现只打印了一次,是因为每次导入模块时,解释器都回去检查一下这个模块有没有之前被导过。

模块导入
首先解析器是找到这个模块,然后判断模块是否被导入过。
如果没有导入,则:
创建一个属于这个模块的命名空间;如果用户没有定义变量来引用这个模块的内存地址的话,那么就使用模块的名称来引用这个模块的内存地址;如果用户使用as来指定变量接受这个内存地址的话,那么就将内存地址赋值给这个变量;且下文在调用时只能使用这个变量进行调用不能再使用模块名进行调用了。然后执行这个模块中的代码;
如果已经导入,则:
解释器不会重新执行模块内的语句,后续的import语句仅仅是对已经加载到内存中的模块的对象增加一次引用;

import module
#或
import module1, module2, module3

模块别名
模块在导入的时候开辟了新空间内存,默认是使用模块的名称来引用这个内存地址的,有时候模块的名称很长再加上执行调用里面的功能的时候,就显的很不方便,为了更好的使用模块,我们可以给模块起别名;
也就是在导入模块的时候我们不让它使用默认的名字来引用内存地址,而是由我们自己定义的变量来引用这个模块的内存地址;

import module as m

这样的话就表示使用变量m来引用这个内存地址,然后我们在文中再使用这个模块的时候,只要使用m来调用这个模块里的功能即可。

导入多个模块

import os,time,sys,re   #每个模块之间用逗号隔开
#或
import os
import time
import sys

规范建议:模块应该一个一个的导入,先后顺序为:内置模块---->扩展(第三方)模块------>自定义模块;
顺序说明:我们知道导入模块其实就是在执行这个模块,我们不确定扩展模块或自定义模块里有没有某个功能调用了内置模块,所以,我们在导入模块时,应先导入解释器内置的模块,然后在导入扩展模块,最后导入自定义模块。

from.....import
在使用一个模块时往往我们只需要使用其中某一个或者几个功能,如果直接用import module则会将模块全部导入。
比如上面的learn.py模块中我只想使用fun1函数功能,则可以这样导入:

from learn import fun1

from.....import 也支持as模式,也支持导入某个模块的多个功能:

from learn import fun1 as f1
from learn import fun1 ,fun2

from.....import *
表示导入模块中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
还有一点要说的是,如果使用* 的方式进行了导入,这时只想使用里面的某个或某些功能时,可以使用__all__来进行约束;
注意:__all__只是用来约束*方式的,其他方式导入的话,不会生效;具体使用见例子:

#定义名为learn.py的模块
print('beginning learn module')
modulename = 'learn'

def fun1():
    print('hello fun1')

def fun2():
    print('hello fun2')

print('end learn module')

#导入调用
from learn import *

fun1()    
print(modulename)
fun2()

#输出如下:
beginning learn module
end learn module
hello fun1
learn
hello fun2

现在对其进行约束一下,要求只能使用fun1()功能;
我们在learn模块开头加入一下

__all__ = ["func1"]

#运行输出:
beginning learn module
end learn module
hello fun1
Exception has occurred: NameError
name 'modulename' is not defined
  File "D:\zPY\test\test_learn.py", line 9, in <module>
    print(modulename)

作用域
在一个模块中,可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

2.包
包是一种管理Python模块命名空间的形式,包是一个有层次的文件目录结构,由模块和子包组成。通俗的讲,包就是(一定)包含__init__.py文件的文件夹。

package_a
├── __init__.py
├── module_a1.py
└── module_a2.py

包导入
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
最简单的情况,放一个空的 :file:init.py就可以了。

import package_a.module_a1

跟之前的模块导入一样,在__init__.py文件中可以定义__all__变量来控制from package import *的导入,如下:

__all__ = ["fun1", "fun2"]

这种情况下from package import *只能导入fun1和fun2函数,其他的都不能使用。

注意:
当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。
反之,如果使用形如 import item.subitem.subsubitem这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。

posted @ 2020-11-18 19:19  吃糙米饼的苏小迪  阅读(132)  评论(0编辑  收藏  举报