我是完全不懂Python的。但是有时也无奈需要写大段大段的Python代码。
之前在《项目所需的Python版本+国内源+虚拟环境+用Conda创建环境》中提到的都是独立的Python项目,当我们把Python类型程序引入生产系统的流程引擎时,又遇到一点小小的问题。
流程引擎中的每个节点,都是由不同语言开发的单个可执行文件,或单个Java的可执行Jar包,或单个Python文件。这些程序通过数据库进行统一的版本管理和验证。
同时平台是兼容不同操作系统的,所以不管哪种语言,Windows和Linux平台会有很多公共部分代码。
对于可执行程序的公共代码,C/C++,Pascal是比较容易管理的。
之前我也提到过Go项目使用自定义公共单元,以及Java管理自定义公共代码。
但是Python这边,依然是所有东西都写入到一个文件中,重复代码很多,且不利于维护。
呃……是时候分模块了。
在Python中,package
(包)是一个用于组织 Python 模块的方式。
一个 package
就是一个包含了多个 Python 模块的文件夹。
这个文件夹中还可能包含其他子文件夹,它们也可以是 package
。
在一个 package
中会有一个 __init__.py
文件。
它的作用是将这个文件夹下的所有模块组织在一起,方便我们在其他模块中导入。
如果没有 __init__.py
文件,这个文件夹就只是一个普通的文件夹,无法被当做 package
来使用。
一个有效的 package
结构如下:
mypackage/
__init__.py
module1.py
module2.py
subpackage/
__init__.py
module3.py
module4.py
我们可以在其他模块中导入这个 package
:
import mypackage.module1
import mypackage.subpackage.module3
在这个例子中,我们导入了 mypackage
中的 module1
模块和 subpackage
中的 module3
模块。
注意导入 subpackage
时需要指定子包的名字。
package
概念的引入使得代码组织更加清晰,避免了模块之间的命名冲突。使用 package
还能使得我们的代码更加易读、易维护和易重用。
我们用PIP
安装的包会放在site-packages
目录中,位置如下:
Windows在:{PythonPath}\Lib\site-packages
。
Linux(Ubuntu)在:/home/{UserName}/.local/lib/{PythonPath}/site-packages
。
我们自定义的模块就可以放这里。
仅存在于Debian或Ubuntu,差不多意思就是系统分发的包。
我们自定义的模块放这里也行(不过不见得有权限)。
位置比如:/usr/lib/python3/dist-packages
硬说Windows对应的位置……可能这个lib目录:{PythonPath}\Lib
算是吧?
根据上面的信息,我自定义了一个包ACTP
,并放入了site-packages
目录。
除了 __init__.py
文件,其它每个文件都包含一个与文件同名的类。
放好后,我们就可以引用它了。
import ACTP.crossPlat
import ACTP.tnu
import ACTP.mylogger
import ACTP.DTStringFinder
但是我的每个模块(文件)中都有和文件同名的类,如果像上面那样引用。
调用时就需要写成crossPlat.crossPlat.someFunction()
,有点长。
所以我就像下面这样,导入类。
from ACTP.crossPlat import crossPlat
from ACTP.tnu import tnu
from ACTP.mylogger import mylogger
from ACTP.DTStringFinder import DTStringFinder
前面做的部分其实已经够用了。
但是你用pip list
,会发现看不到自定义的包ACTP
。
> pip list
Package Version
------------------ --------
certifi 2023.5.7
charset-normalizer 3.1.0
distlib 0.3.6
filelock 3.12.0
idna 3.4
piexif 1.1.3
Pillow 9.5.0
pip 23.1.2
platformdirs 3.5.1
py4j 0.10.9.2
pyspark 3.2.0
requests 2.30.0
setuptools 65.5.0
urllib3 2.0.2
virtualenv 20.23.0
再换个指令看看。
包就像根本不存在一下。
> pip show ACTP
WARNING: Package(s) not found: ACTP
如果想用pip list
,pip show
查看包,还得补充一些信息。
我们需要一个{包名}-版本号.dist-info
的文件夹,和包一起放在site-packages
目录中。
这部分可以参考已有的包。
目录中有好些文件,我们先不管其它文件。
找到有个METADATA
(元数据)的文件。
保存后再试试list和show,包的信息就出来了:
> pip list
Package Version
------------------ --------
ACTP 1.0.3
certifi 2023.5.7
charset-normalizer 3.1.0
distlib 0.3.6
filelock 3.12.0
idna 3.4
piexif 1.1.3
Pillow 9.5.0
pip 23.1.2
platformdirs 3.5.1
py4j 0.10.9.2
pyspark 3.2.0
requests 2.30.0
setuptools 65.5.0
urllib3 2.0.2
virtualenv 20.23.0
>
> pip show ACTP
Name: ACTP
Version: 1.0.3
Summary: 在奥诚科技的任务调度平台中更加方便的开发Python类型业务规则
Home-page: http://www.scaocheng.com/
Author: 曾如石
Author-email: [email protected]
License: Private
Location: c:\users\shion\appdata\local\programs\python\python310\lib\site-packages
Requires:
Required-by:
Python的包package
仅仅是目录名称,这种方式不太靠谱。
pip即使检查依赖也不会检查出包里面的问题(缺失,错误)。
所以有时pip反复卸载/安装了包,还是有问题时,可以考虑删除相关包和其依赖的的所有目录,再全部重新安装。
自定义包也一样,需要人工保证代码版本正确,文件没有缺失和错误。
package
要遵循一定的命名规则,通常采用小写字母、下划线分隔符的命名方式,避免使用 Python 关键字或内置函数的命名。
在导入一个 package
的时候需要注意路径。使用 import
语句时,可以使用绝对导入或相对导入方式,具体使用哪种方式需要根据实际情况来决定。
避免循环导入。循环导入指的是多个 package
或模块之间相互引用,形成了循环引用的关系。这种情况会导致 Python 解释器发生死循环,并最终崩溃。我们应该避免循环导入,或者对导入顺序进行调整。
在一个 package
内部,通常会有 __init__.py
文件,其中定义了模块的导出和初始化逻辑。该文件中会包含一些应该暴露给外部的函数、类或者变量,以及 package
的初始化代码。
在该 package
中,还可能包含一些其他的子模块或子 package
,在 __init__.py
文件中,我们可以通过 import
语句将这些子模块或子 package
导出。这样,在导入该 package
的时候,这些子模块或子 package
也会被导入。
总之,使用 Python package
可以使得代码组织更加清晰。
唯独小心按照代码量计算工作量的时候,模块化的思路会导致扣钱(蛤)???