Python 模块的制作,发布,安装

build2024 / 2024-07-08 / 原文

在 Python 当中,一个Python 文件就是一个模块,模块的名字就是Python文件的名字。例如:创建一个 test.py文件,那么test.py 就是一个模块

模块的作用:

  • 可以使我们有逻辑的去组织我们的 Python 代码,以库的形式去封装功能,非常方便的去让调用者去使用模块中的功能

  • 可以定义函数,类,变量,也能包括可执行的代码

模块的分类:

  • 内置模块
  • 自定义的模块
  • 第三方模块

注意:
不同的模块之间可以定义相同的变量名,但是每个模块中的变量名作用域只是在本模块中

  1. Python 模块的制作以及调用

创建一个 自定义的Python模块 moduleTest.py其中包括了 add函数deff函数

def add(a, b):
    return a + b

def deff(x, y):
    return x - y

再创建一个 MainTest.py 模块,去调用 moduleTest.py 模块,如下所示:

import moduleTest

sum = moduleTest.add(12,34)
deff = moduleTest.deff(34,56)

print(f"两个函数云计算结果分别是{sum, deff}")

调用效果如下:

image

以上是正常创建模块和调用模块的步骤,但一般情况下,模块写完后,我们还需要对模块中的函数进行测试。例如,直接在moduleTest.py 模块中去测试创建好的函数 add

def add(a, b):
    return a + b

def deff(x, y):
    return x - y

res=add(2,4)
print(f'测试模块函数结果{res}')

然后,右键执行当前的 moduleTest.py 模块并看输出结果看是否正常:

image

那么,可以看到输出的测试结果是正常的。并且我们不需要删除测试代码的情况,直接在 MainTest.py 去调用moduleTest.py 模块,会发现,它把不该输出的测试内容也给调用输出了

image

这个是什么情况呢?应该怎么避免该情况的发生呢?

这种情况就是在加载外部模块的时候,把当前moduleTest.py 模块,所有的可执行的代码函数全部加载过来了
如何防止该种情况的发生?那么就需要使用了 魔法属性 __name__

魔法属性 __name__ 作用

  • 如果当前 moduleTest.py 文件直接运行的时候,魔法属性 __name__ 输出的值是__main__
  • 如果当前 moduleTest.py 文件作为模块,供外部去引用访问的时候,魔法属性 __name__ 输出的值是当前的文件名称

可以进行测试验证一下上面陈述:

  • moduleTest.py 文件直接运行输出结果:

image

  • moduleTest.py 文件作为模块,供外部去引用访问,输出结果:

image

那么,为了防止当前 moduleTest.py 模块做为自定义模块提供给外部使用的时候,也能访问到测试的函数,就需要在 moduleTest.py 文件中,加入以下判断if __name__ == '__main__':

def add(a, b):
    return a + b


def deff(x, y):
    return x - y


if __name__ == '__main__':
    res = add(2, 4)
    print(f'测试模块函数结果{res}')
    print(f'模块的魔法属性 __name__ 变量值: {__name__}')

这样就实现了,只有当前 moduleTest.py 文件作为主文件直接运行的时候,才能调用到测试的函数。如果作为模块供外部访问调用的时候就不会调用到测试的函数了。效果如下:

image


再额外介绍另外一个魔法属性 __all__

魔法属性 __all__ 作用,如果当前的 moduleTest.py 文件做为模块供外部其他模块进行访问调用的时候,并且使用导入模块的方式为 from xxx import * 的格式导入所有的函数时,魔法属性 __all__会限制该种方式,只允许访问数组里面列出的函数。但对 import xxx 的格式导入不起作用。

例如:在 moduleTest.py 文件中,加入魔法属性 __all__,并且只添加了 add 函数

__all__=['add']

def add(a, b):
    return a + b

def deff(x, y):
    return x - y

if __name__ == '__main__':
    res = add(2, 4)
    print(f'测试模块函数结果{res}')
    print(f'模块的魔法属性 __name__ 变量值: {__name__}')


然后在 MainTest.py 中,通过 from moduleTest import * 的格式导入当前模块所有的函数

  • 测试调用在 moduleTest.py 文件中的魔法属性 __all__ 添加的函数 add
from  moduleTest import *

print(add(7,8))

测试结果正常

image

  • 接着,再测试 moduleTest.py 文件中另外一个函数 deff,就直接就抛出异常 NameError: name 'deff' is not defined,显示该函数未定义。

image

所以,以上结果也就验证了,使用导入模块的方式为 from xxx import * 的格式导入所有的函数时,魔法属性__all__只允许该种方式访问数组里面列出的函数。

但如果使用 import moduleTest 导入方式,则不受影响。测试效果如下:

image


  1. 模块的发布

例如,我们把当前写好的moduleTest.py模块,进行发布

  • 首先,创建一个自定义目录(moduleTest),将写好的包(模块)放到该目录下

image

  • 接着,再在该目录下面,创建一个 setup.py 文件,并在文件里面写入以下代码
from distutils.core import setup
# name 模块名称
# version 版本号
# description 描述
# author 作用
# py_modules 要发布的内容,发布多个模块使用,逗号隔开
setup(name='my_module', version='1.0', description='数字计算模块',
      author='Me', py_modules=['moduleTest'])

如果在 setup.py 文件中,使用 distutils 报错。由于我使用的Python 版本比较高.然而在3.12 版本发布后,就已经移除了该模块。以下是网上的资料

随着3.12的发布,distutils终于被移除。根据PEP 632,distutils在3.10中被标记为废弃,在 3.12中将不再是标准库的一部分。从distutils中导入将导致一个错误,因为不会有向后兼容的情况出现。
Distutils曾经是Python中软件包管理的首选模块,但它的局限性导致了setuptools的兴起,根据Python打包用户指南,它现在已经成为推荐的解决方案。Setuptools仍然使用distutils的一些功能,但它集成了后者的一个副本,不再依赖标准库。Pip用setuptools替换distutils已经有一段时间了,所以在3.12中删除传统的distutils模块是合理的。

image

解决上面报错办法,需要安装 pip install setuptools 步骤如下:

image

安装成功后,把 setup.py 文件中原来的 distutils.core 替换成 setuptools 即可。

from setuptools import setup
# name 模块名称
# version 版本号
# description 描述
# author 作用
# py_modules 要发布的内容,发布多个模块使用,逗号隔开
setup(name='my_module', version='1.0', description='文件统计模块',
      author='Me', py_modules=['moduleTest'])

  • 创建模块,进入 setup.py 文件目录,然后执行以下命令 python setup.py build

打开命令终端,使用 cd 命令切换到 创建的moduleTest 目录下面,执行上述命令

image

  • 模块发布,需要把该模块生成压缩包,执行命令python setup.py sdist

同样使用 cd 命令切换到 moduleTest 目录下面,执行上述命令

image

最后生成的压缩包 my_module-1.0.tar.gz 就可以把它发布出去,给别人安装使用了。

  1. 模块安装

将生成的压缩包文件 my_module-1.0.tar.gz 复制D盘的一个目录下面

image

新建一个测试工程,进行安装外部的 python 模块 my_module-1.0.tar.gz

模块安装命令 pip install xxx

image

安装完毕后,就可以直接在当前的项目工程中去使用到当前模块中的所有函数功能了。如下所示:

image