python:模块(module)

模块化程序设计

模块化,模块化指将一个完整的程序分解为一个一个小的模块。通过将模块组合,来搭建出一个完整的程序
在这里插入图片描述

  • 包类似于文件夹,模块类似于文件
  • 在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件。 注意:模块名要符号标识符的规范
  • 模块由语句组成。运行 Python 程序时,按照模块中语句的顺序依次执行。

为什么要模块化编程

  • 不采用模块化,统一将所有的代码编写到一个文件中
  • 采用模块化,将程序分别编写到多个文件中, 优点
    ① 方便开发
    ② 方便维护
    ③ 模块可以复用!

模块化编程的流程

  • 设计 API,进行功能描述。 (不涉及代码,用文档描述)
  • 编码实现 API 中描述的功能。
  • 在模块中编写测试代码,并消除全局代码。
  • 使用私有函数实现不被外部客户端调用的模块函数。

模块的 API 和功能描述要点

API(Application Programming Interface 应用程序编程接口)是用于描述模块中提供的函数和类的功能和使用方式。(描述类和函数的功能及使用方式)

模块化编程中,首先设计的就是模块的 API(即要实现的功能描述),然后开始编 码实现 API 中描述的功能。最后,在其他模块中导入本模块进行调用。

我们可以通过help(模块名)查看模块的API。一般使用时先导入模块 然后通过help查看。

在这里插入图片描述
也可以在 python 的 api 文档中查询。首先进入 python 的安装目录下的 docs 子目录;双击打开 chm 文档,即可通过索引输入“math”查询到对应的 API 内容。

我们可以通过__doc__可以获得模块的文档字符串的内容:

salary.py
"""
计算员工工资
"""

def yearSalary(monthSalary):
    """根据传入的月薪,计算出年薪
    """
    pass

def daySalary(monthSalary):
    """根据传入的月薪,计算出每天的薪资
    """
    pass
    
main.py
import salary
print(salary.__doc__)
print(salary.yearSalary.__doc__)
print(salary.daySalary.__doc__)

输出:
计算员工工资

根据传入的月薪,计算出年薪
    
根据传入的月薪,计算出每天的薪资

模块测试代码

  • 每个模块都有一个名称,在每一个模块内部都有一个__name__属性,通过这个属性可以获取到模块的名字
    • 在正常情况下,模块名字对应源文件名
    • 仅有一个例外,就是当一个模块被作为程序入口时(主 程序、交互式提示符下),它的__name__的值为“main”。
      • 一个程序中只会有一个主模块
      • 主模块就是我们直接通过 python 执行的模块
import time as test

print(test.__name__)   #time
print(__name__)        #__main__ 

举个例子 (通过__name==“main”独立处理模块.)

  1. demo.py 内容分为源代码和测试代码
#coding=utf-8
#-------------src---------------
# 源代码
name = "11111 not func"
add = "http://c.biancheng.net/python"
print(name,add)

def say():
    print("22222  Python!")
class CLanguage:
    def __init__(self,name,add):
        self.name = name
        self.add = add
    def say(self):
        print(self.name,self.add)



#----------test-------------
# 通常情况下,为了检验模板中代码的正确性,我们往往需要为其设计一段测试代码
say()
clangs = CLanguage("C语言中文网","http://c.biancheng.net")
clangs.say()
  1. 运行demo.py输出如下
('11111 not func', 'http://c.biancheng.net/python')
22222  Python!
('33333', 'http://c.biancheng.net')
  1. 在此基础上,我们可以新建一个 test.py 文件,并在该文件中使用 demo.py 模板文件,即使用 import 语句导入 demo.py:
import demo
  1. 运行 test.py 输出如下
('11111 not func', 'http://c.biancheng.net/python')
22222  Python!
('33333', 'http://c.biancheng.net')
  • 可以看到,当执行 test.py 文件时,它同样会执行 demo.py 中用来测试的程序,这显然不是我们想要的效果。正常的效果应该是,只有直接运行模板文件时,测试代码才会被执行;反之,如果是其它程序以引入的方式执行模板文件,则测试代码不应该被执行。
  • 要实现这个效果,可以借助 Python 内置的 name 变量。当直接运行一个模块时,name 变量的值为 main;而将模块被导入其他程序中并运行该程序时,处于模块中的 name 变量的值就变成了模块名。因此,如果希望测试函数只有在直接运行模块文件时才执行,则可在调用测试函数时增加判断,即只有当 name ==‘main’ 时才调用测试函数。
  • 怎么办呢?
    • 想要避免这种情况的关键在于,要让 Python 解释器知道,当前要运行的程度代码,是模块文件本身,还是导入模块的其它程序。
    • 为了实现这一点,就需要使用 Python 内置的系统变量 name,它用于标识所在模块的模块名。
  1. 修改 demo.py 模板文件的测试代码为:
    • if name == ‘main’: 的作用是确保只有单独运行该模块时,此表达式才成立,才可以进入此判断语法,执行其中的测试代码;
    • 反之,如果只是作为模块导入到其他程序文件中,则此表达式将不成立,运行其它程序时,也就不会执行该判断语句中的测试代码。
if __name__ == '__main__':
    say()
    clangs = CLanguage("C语言中文网","http://c.biancheng.net")
    clangs.say()
  1. 运行demo.py输出如下
('11111 not func', 'http://c.biancheng.net/python')
22222  Python!
('33333', 'http://c.biancheng.net')
  1. 运行 test.py 输出如下
$ python test.py 
('11111 not func', 'http://c.biancheng.net/python')

模块的导入

语法

导入全部

import 模块名

  • (模块名,就是python文件的名字,注意不要py) #导入一个模块
# 导入sys整个模块
import sys
# 使用sys模块名作为前缀来访问模块中的成员
print(sys.argv[0])
  • 上面在程序中使用 sys 模块内的成员时,必须添加模块名作为前缀。
  • 运行上面程序,可以看到如下输出结果(sys 模块下的 argv 变量用于获取运行 Python 程序的命令行参数,其中 argv[0] 用于获取当前 Python 程序的存储路径):
C:\Users\mengma\Desktop\hello.py

import 模块名 as 模块别名

  • 导入整个模块时,也可以为模块指定别名。
# 导入sys整个模块,并指定别名为s
import sys as s
# 使用s模块别名作为前缀来访问模块中的成员
print(s.argv[0])
  • 上面在程序中使用 sys 模块内的成员时,必须添加模块别名 s 作为前缀
  • 运行上面程序,可以看到如下输出结果(sys 模块下的 argv 变量用于获取运行 Python 程序的命令行参数,其中 argv[0] 用于获取当前 Python 程序的存储路径):
C:\Users\mengma\Desktop\hello.py

import 模块 1,模块 2…

  • 也可以一次导入多个模块,多个模块之间用逗号隔开。
# 导入sys、os两个模块
import sys,os
# 使用模块名作为前缀来访问模块中的成员
print(sys.argv[0])
# os模块的sep变量代表平台上的路径分隔符
print(os.sep)
  • 在 Windows 平台上运行该程序,可以看到如下输出结果(os 模块的 sep 变量代表平台上的路径分隔符):
C:\Users\mengma\Desktop\hello.py
\


import 模块 1 as 模块别名1,模块 2 as 模块别名2…

  • 在导入多个模块的同时,也可以为模块指定别名
# 导入sys、os两个模块,并为sys指定别名s,为os指定别名o
import sys as s,os as o
# 使用模块别名作为前缀来访问模块中的成员
print(s.argv[0])
print(o.sep)

也可以只引入模块中的部分内容

from 模块名 import 成员名

# 导入sys模块的argv成员
from sys import argv
# 使用导入成员的语法,直接使用成员名访问
print(argv[0])
  • 第 2 行代码导入了 sys 模块中的 argv 成员,这样即可在程序中直接使用 argv 成员,无须使用任何前缀。运行该程序,可以看到如下输出结果:
C:\Users\mengma\Desktop\hello.py

from 模块名 import 成员名 as 别名

# 导入sys模块的argv成员,并为其指定别名v
from sys import argv as v
# 使用导入成员(并指定别名)的语法,直接使用成员的别名访问
print(v[0])
  • 第 2 行代码导入了 sys 模块中的 argv 成员,并为该成员指定别名 v,这样即可在程序中通过别名 v 使用 argv 成员,无须使用任何前缀。运行该程序,可以看到如下输出结果:
C:\Users\mengma\Desktop\hello.py

from 模块名 import 成员名1, 成员名2

  • form…import 导入模块成员时,支持一次导入多个成员,例如如下程序:
# 导入sys模块的argv,winver成员
from sys import argv, winver
# 使用导入成员的语法,直接使用成员名访问
print(argv[0])
print(winver)
  • 上面第 2 行代码导入了 sys 模块中的 argv、 winver 成员,这样即可在程序中直接使用 argv、winver 两个成员,无须使用任何前缀。运行该程序,可以看到如下输出结果(sys 的 winver 成员记录了该 Python 的版本号):
C:\Users\mengma\Desktop\hello.py
3.6

from 模块 import 成员 1 as 成员别名1,成员2 as 成员别名2…

  • 一次导入多个模块成员时,也可指定别名,同样使用 as 关键字为成员指定别名,例如如下程序:
# 导入sys模块的argv,winver成员,并为其指定别名v、wv
from sys import argv as v, winver as wv
# 使用导入成员(并指定别名)的语法,直接使用成员的别名访问
print(v[0])
print(wv)

  • 上面第 2 行代码导入了 sys 模块中的 argv、winver 成员,并分别为它们指定了别名 v、wv,这样即可在程序中通过 v 和 wv 两个别名使用 argv、winver 成员,无须使用任何前缀。运行该程序,可以看到如下输出结果:
C:\Users\mengma\Desktop\hello.py
3.6

from m import * # 引入到模块中所有内容,一般不会使用

  • 注意:尽量避免“from 模块名 import ”这种写法。 它表示导入模块中所有的不 是以下划线(_)开头的名字都导入到当前位置。 但你不知道你导入什么名字,很有可能 会覆盖掉你之前已经定义的名字

ModuleNotFoundError: No module named ‘模块名’

在其它文件中用 import(或 from…import) 语句引入该文件时,Python 解释器同时如下错误:
ModuleNotFoundError: No module named ‘模块名’

意思是 Python 找不到这个模块名,这是什么原因导致的呢?

通常情况下,当使用 import 语句导入模块后,Python 会按照以下顺序查找指定的模块文件:

  • 在当前目录,即当前执行的程序文件所在目录下查找;
  • 到 PYTHONPATH(环境变量)下的每个目录中查找;
  • 到 Python 默认的安装目录下查找。

以上所有涉及到的目录,都保存在标准模块 sys 的 sys.path 变量中,通过此变量我们可以看到指定程序文件支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常。

解决“Python找不到指定模块”的方法有 3 种,分别是:

  • 向 sys.path 中临时添加模块文件存储位置的完整路径;
  • 将模块放在 sys.path 变量中已包含的模块加载路径中;
  • 设置 path 系统环境变量。

举个例子

创建hello.py和say.py,分别放到不同目录下:

#hello.py
def say ():
    print("Hello,World!")
#say.py
import hello
hello.say()

显然,hello.py 文件和 say.py 文件并不在同一目录,此时运行 say.py 文件,其运行结果为:

 Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\say.py", line 1, in <module>
    import hello
ModuleNotFoundError: No module named 'hello'
  • 可以看到,Python 解释器抛出了 ModuleNotFoundError 异常。

怎么解决

(1) 导入模块方式一:临时添加模块完整路径

  • 模块文件的存储位置,可以临时添加到 sys.path 变量中,即向 sys.path 中添加 D:\python_module(hello.py 所在目录),在 say.py 中的开头位置添加如下代码:
import sys
sys.path.append('D:\\python_module')
  • 需要注意的是,通过该方法添加的目录,只能在执行当前文件的窗口中有效,窗口关闭后即失效。

(2) 导入模块方式二:将模块保存到指定位置

  • 如果要安装某些通用性模块,比如复数功能支持的模块、矩阵计算支持的模块、图形界面支持的模块等,这些都属于对 Python 本身进行扩展的模块,这种模块应该直接安装在 Python 内部,以便被所有程序共享,此时就可借助于 Python 默认的模块加载路径。
  • 通常来说,我们默认将 Python 的扩展模块添加在 lib\site-packages 路径下,它专门用于存放 Python 的扩展模块和包。

所以,我们可以直接将我们已编写好的 hello.py 文件添加到 lib\site-packages 路径下,就相当于为 Python 扩展了一个 hello 模块,这样任何 Python 程序都可使用该模块。

移动工作完成之后,再次运行 say.py 文件,可以看到成功运行的结果:

Hello,World!

(3) 导入模块方式三:设置环境变量

  • PYTHONPATH 环境变量(简称 path 变量)的值是很多路径组成的集合,Python 解释器会按照 path 包含的路径进行一次搜索,直到找到指定要加载的模块。当然,如果最终依旧没有找到,则 Python 就报 ModuleNotFoundError 异常。
  • 由于不同平台,设置 path 环境变量的设置流程不尽相同

(3.1) windows平台下
在这里插入图片描述
在这里插入图片描述

  • 如上图, 通过该对话框,就可以完成 path 环境变量的设置。需要注意的是,该对话框分为上下 2 部分,其中上面的“用户变量”部分用于设置当前用户的环境变量,下面的“系统变量”部分用于设置整个系统的环境变量。
  • 通常情况下,建议大家设置设置用户的 path 变量即可,因为此设置仅对当前登陆系统的用户有效,而如果修改系统的 path 变量,则对所有用户有效。
  • 对于普通用户来说,设置用户 path 变量和系统 path 变量的效果是相同的,但 Python 在使用 path 变量时,会先按照系统 path 变量的路径去查找,然后再按照用户 path 变量的路径去查找。
  • 这里我们选择设置当前用户的 path 变量。单击用户变量中的“新建”按钮, 系统会弹出下图

在这里插入图片描述

  • 其中,在“变量名”文本框内输入 PYTHONPATH,表明将要建立名为 PYTHONPATH 的环境变量;在“变量值”文本框内输入 .;d:\python_ module。注意,这里其实包含了两条路径(以分号 ;作为分隔符):
    • 第一条路径为一个点(.),表示当前路径,当运行 Python 程序时,Python 将可以从当前路径加载模块;
    • 第二条路径为 d:\python_ module,当运行 Python 程序时,Python 将可以从 d:\python_ module 中加载模块。
  • 然后点击“确定”,即成功设置 path 环境变量。此时,我们只需要将模块文件移动到和引入该模块的文件相同的目录,或者移动到 d:\python_ module 路径下,该模块就能被成功加载。

(3.2) 在 Linux 上设置环境变量

  • 启动 Linux 的终端窗口,进入当前用户的 home 路径下,然后在 home 路径下输入如下命令:
ls - a
  • 该命令将列出当前路径下所有的文件,包括隐藏文件。Linux 平台的环境变量是通过 .bash_profile 文件来设置的,使用无格式编辑器打开该文件,在该文件中添加 PYTHONPATH 环境变量。也就是为该文件增加如下一行:
#设置PYTHON PATH 环境变量
PYTHONPATH=.:/home/mengma/python_module
  • Linux 与 Windows 平台不一样,多个路径之间以冒号(:)作为分隔符,因此上面一行同样设置了两条路径,点(.)代表当前路径,还有一条路径是 /home/mengma/python_module(mengma 是在 Linux 系统的登录名)。
  • 在完成了 PYTHONPATH 变量值的设置后,在 .bash_profile 文件的最后添加导出 PYTHONPATH 变量的语句。
#导出PYTHONPATH 环境变量
export PYTHONPATH
  • 重新登录 Linux 平台,或者执行如下命令:
source.bash_profile

(3.3) 在Mac OS X 上设置环境变量

  • 在 Mac OS X 上设置环境变量与 Linux 大致相同(因为 Mac OS X 本身也是类 UNIX 系统)。启动 Mac OS X 的终端窗口(命令行界面),进入当前用户的 home 路径下,然后在 home 路径下输入如下命令:
ls -a
  • 该命令将列出当前路径下所有的文件,包括隐藏文件。Mac OS X 平台的环境变量也可通过,bash_profile 文件来设置,使用无格式编辑器打开该文件,在该文件中添加 PYTHONPATH 环境变量。也就是为该文件增加如下一行:
#设置PYTHON PATH 环境变盘
PYTHONPATH=.:/Users/mengma/python_module
  • 在完成了 PYTHONPATH 变量值的设置后,在 .bash_profile 文件的最后添加导出 PYTHONPATH 变量的语句。
#导出PYTHON PATH 环境变量
export PYTHONPATH
  • 重新登录 Mac OS X 系统,或者执行如下命令:
source .bash_profile

__import__()

import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头

  • 我们一般通过 import 语句实现模块的导入和使用,import 本质上是使用了内置函数__import__()
  • 当我们通过 import 导入一个模块时,python 解释器进行执行,最终会生成一个对象, 这个对象就代表了被加载的模块。
import math
print(id(math))
print(type(math))
print(math.pi) #通过math.成员名来访问模块中的成员

输出:
2065342821784
<class 'module'>
3.141592653589793

  • import 语句本质上就是调用内置函数__import__(),我们可以通过它实现动态导入。给__import__()动态传递不同的的参数值,就能导入不同的模块。
s = 'math'
m = __import__(s)
print(m.pi)

输出:
3.141592653589793

为啥需要__import__()

  • 模块文件可以起名为类似“a b”的形式。但这和 Python 语法相矛盾,换句话说,Python 是以空格来隔离一行语句中的不同元素的,如果模块名中出现空格,就无法再使用 import 引入。
  • 不仅如此,如果模块名称以数字开头,也无法使用 import 语句正常导入。例如将 "demo text"模块文件名改为 “1demo”,并使用 import 尝试导入,也会报 SyntaxError 错误:
>>>import demo text

SyntaxError: invalid syntax

>>> import 1demo

SyntaxError: invalid syntax

此时,可以使用Python 提供的 import() 内置函数引入模块

__import__("demo text")
__import__("1demo")

注意

  • 注意:一般不建议我们自行使用__import__()导入,其行为在 python2 和 python3 中 有差异,会导致意外错误。如果需要动态导入可以使用 importlib 模块。
import importlib
a  = importlib.import_module('math')
print(a.pi)

输出:
3.141592653589793

Python __all__变量用法

事实上,当我们向文件导入某个模块时,导入的是该模块中那些名称不以下划线(单下划线“_”或者双下划线“__”)开头的变量、函数和类。因此,如果我们不想模块文件中的某个成员被引入到其它文件中使用,可以在其名称前添加下划线。

举个例子:

#demo.py
def say():
    print("人生苦短,我学Python!")
def CLanguage():
    print("C语言中文网:http://c.biancheng.net")
def disPython():
    print("Python教程:http://c.biancheng.net/python")
#test.py
from demo import *
say()
CLanguage()
disPython()
  • 执行 test.py 文件,输出结果为:
人生苦短,我学Python!
C语言中文网:http://c.biancheng.net
Python教程:http://c.biancheng.net/python
  • 在此基础上,如果 demo.py 模块中的 disPython() 函数不想让其它文件引入,则只需将其名称改为 _disPython() 或者 __disPython()。修改之后,再次执行 test.py,其输出结果为:
人生苦短,我学Python!
C语言中文网:http://c.biancheng.net
Traceback (most recent call last):
  File "C:/Users/mengma/Desktop/2.py", line 4, in <module>
    disPython()
NameError: name 'disPython' is not defined

除此之外,还可以借助模块提供的 __all__ 变量,该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。通过在模块文件中设置 __all__ 变量,当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 __all__ 列表中指定的成员

  • 也就是说,只有以“from 模块名 import *”形式导入的模块,当该模块设有 __all__ 变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。

举个例子:

def say():
    print("人生苦短,我学Python!")
def CLanguage():
    print("C语言中文网:http://c.biancheng.net")
def disPython():
    print("Python教程:http://c.biancheng.net/python")
__all__ = ["say","CLanguage"]
  • 可见,__all__ 变量只包含 say() 和 CLanguage() 的函数名,不包含 disPython() 函数的名称。此时直接执行 test.py 文件,其执行结果为:
人生苦短,我学Python!
C语言中文网:http://c.biancheng.net
Traceback (most recent call last):
  File "C:/Users/mengma/Desktop/2.py", line 4, in <module>
    disPython()
NameError: name 'disPython' is not defined
  • 再次声明,__all__ 变量仅限于在其它文件中以“from 模块名 import *”的方式引入。也就是说,如果使用以下 2 种方式引入模块,则 __all__ 变量的设置是无效的。
      1. 以“import 模块名”的形式导入模块。通过该方式导入模块后,总可以通过模块名前缀(如果为模块指定了别名,则可以使用模快的别名作为前缀)来调用模块内的所有成员(除了以下划线开头命名的成员)。
    1. 以“from 模块名 import 成员”的形式直接导入指定成员。使用此方式导入的模块,__all__ 变量即便设置,也形同虚设。

模块的加载问题

  • 可以引入同一个模块多次,但是模块的实例只会创建一个
import math
print(id(math))
import math as m
print(id(m))

输出:
2250728961432
2250728961432

  • 有时候我们确实需要重新加载一个模块,这时候可以使用:importlib.reload() 方法。

import 加载的模块分为四个通用类别:

  • 使用 python 编写的代码(.py 文件);
  • 已被编译为共享库或 DLL 的 C 或 C++扩展;
  • 包好一组模块的包
  • 使用 C 编写并链接到 python 解释器的内置模块;
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python ModuleNotFoundError: No modulePython中常见的错误之一,通常是由于Python解释器无法找到所需的模块或包而引起的。这可能是因为模块未正确安装,或者是因为Python解释器无法找到模块的路径。 以下是一些可能导致ModuleNotFoundError的原因和解决方法: 1. 模块未正确安装:请确保已正确安装所需的模块。可以使用pip命令来安装模块,例如: ```python pip install module_name ``` 2. 模块路径未添加到系统路径中:请确保模块所在的路径已添加到系统路径中。可以使用以下代码将模块路径添加到系统路径中: ```python import sys sys.path.append('/path/to/module') ``` 3. 模块名称拼写错误:请确保模块名称的拼写正确。Python对大小写敏感,因此请确保模块名称的大小写与实际文件名匹配。 4. 模块文件名与模块名称不匹配:请确保模块文件名与模块名称匹配。例如,如果模块名称为module_name,则模块文件名应为module_name.py。 5. Python版本不兼容:请确保所需的模块Python版本兼容。有些模块只能在特定版本的Python中使用。 6. 模块依赖项未正确安装:请确保所需的模块依赖项已正确安装。有些模块需要其他模块才能正常工作。 以下是一个示例代码,演示了如何处理ModuleNotFoundError: ```python try: import module_name except ModuleNotFoundError: print("Module not found. Please install the module or add the module path to system path.") ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值