82. 学会寻找由其他Python开发者所构建的模块

pypi.oyg是python开发者集中存放模块的地方,可以在这里下载安装其他人开发的模块。pip可以和venv配合使用给不同项目安装不同版本的软件包。

83. 用虚拟环境隔离项目,并重建依赖关系

通常情况下,我们安装python后,安装的各种包都是在全局环境下的,但是这样会存在一些问题。比如你和A合作开发,这是你从他那边拷贝了项目代码后,需要下载相应版本的库,然后运行;那么,当你和B合作开发时,有需要重新下载其他版本来适应B的代码。
可以采用venv工具,python -m venv envname来创建虚拟环境,这样就可以把同一个库的不同版本安装在不同的环境中了。
python -m venv envname
cd envname
source bin/activate # linux启动虚拟环境
myproject\Scripts\activate.bat # windows启动虚拟环境
myproject\Scripts\activate.ps1 # windows powershell启动环境
deacivate # 回到原来的环境
把环境中的库版本导出
python3 -m pip freeze > requirement.txt # linux导出环境中的库版本
创建好虚拟环境后,里面是没有库的,可以通过导入现有的环境数据
python3 -m pip install -r /tmp/envname/requirement.txt

84. 每一个函数,类和模块都要写docstring

docstring就是对这个函数或类或模块的解释写法:”””………”””
可以直接查看,如函数名.doc,可以用help函数
通过pydoc模块在本机启动一个web服务,这个服务会提供python解释器能访问的所有文档,包括自己定义的那些模块。
为模块编写文档
每个模块要有顶级的docstring,即卸载源文件开头,介绍本模块与其中的内容
第一行,一句话描述模块的作用。另起一段,讲述使用者需要注意的事项,重要的函数和类需要在docstring强调。如果这个模块可以在命令行使用,那么需要在docstring里介绍。
为类编写文档
每个类应该有类级别的docstring,写法和模块文档类似,一句话写一下类的用途,后面各段写类中的每一种操作。类中比较重要的public属性和方法也需要体现在描述中,如果要编写子类,子类应该如何与保护的属性以及种类中的方法交互
为函数编写文档
每个public方法和函数都应该有docstring,第一段一句话介绍做什么的,接下来一段描述函数的行为,然后各用一段描述参数和返回值,需要处理什么样的异常

  1. def fuc(att1, att2):
  2. """miaoshu
  3. miaoshu
  4. Args:
  5. att1:...
  6. att2:...
  7. Returns:
  8. ....
  9. """
  1. **用类型注解来简化docstring**<br />通过类型注解后,在docstring可以省略对属性和返回值的描述
  1. def func(att1: str, att2: int) -> str:
  2. """
  3. """
  4. pass

85. 用包来安排模块,已提供稳定的API

通常随着函数,数据结构,类等越来越多,我们会把他们放到各个模块里面,当随着模块变多后,可以使用包来安排模块,包本身也是模块,它里面包含其他模块。
init.py放到目录中,可以使这个目录成为一个包。成为一个包之后,就可以使用相对于该路径引入包中的其他py文件。
main.py
mypkg/utils
这样可以在main文件里面使用from mypkg import utils
如果有些包位于更大的包中,则需要from name.name.name import ….
作用:
用包划分名称空间。如果两个模块的文件名相同,则后import的会覆盖之前import的,但是如果把他们分别放在不同的包里则可以进行区分
用包构建稳固的API。如果这套API要提供给很多人使用,例如要做成开源的软件包,那么你可能想把它的功能稳定下来,以免新旧版本之间差得太大。为了做到这一点,必须隐藏软件包内部的代码结构不要让外部的开发者依赖这套结构
python中可以使用all这个特殊属性决定模块或包里面有哪些内容应该当作API公布给外界,all是一份列表,如果使用from name import 那么只有all中所列出的名称才会被导入,如果没有all则只会引入public属性,即不以下划线开头的属性。
all可以定义在模块里面,用于向外提供公开可用的类或API,也可以定义在包的init.py里面,用于向包外的使用者提供API。这样就可以通过from 包名 import
导入所有公开的API。当然import *这样的写法是要谨慎使用的,因为没有提供具体的名字,这样写就很不清晰。
如果这个包只是在内部使用,那可能就没必要专门设置all
image.png

86. 考虑用模块级别的代码配置不同的部署环境

不同的环境下可能要运行不同的代码,比如windows环境下运行什么代码,linux运行什么代码,那么可以先写好代码在不同的模块里,然后根据sys.platform.startwith(‘win32’)来判断,其他的比如根据环境变量os.environ来判断等,如果比较复杂可以设置configparser来处理配置参数。

87. 为自编的模块定义根异常,让调用者能够专门处理与此API有关的异常

定义根异常后,模块中使用的异常都继承自这个根异常。调用者可以根据根异常捕获所有模块中使用的所有异常。那么,如果抛出了非根异常的错误,则可能是API本身的错误而不是调用者调用的错误。

88. 用适当方法打破循环依赖关系

  • 重构代码
  • 调整import语句位置,不建议
  • 把模块分成引入-配置-运行三个环节,把需要执行的两个循环依赖的模块中的方法或函数放到文件的最后
  • 动态引入,import语句放到函数里,或者说是放到需要这个模块之前,如下面,但说实话也是换了import的位置啊。。。

    1. def A():
    2. import B
    3. B.p()

    89. 重构时考虑通过warning提醒开发者API已经发生变化

    如果API很小,与上下游关系不复杂,则直接更新就好;反之,则需要通过warning来提醒开发者API已经发生了变化,以免发生错误调用。比如本来一个函数是通过kg计算的,现在改成用g计算了,则需要提醒。

    90. 考虑用typing做静态分析,以消除bug

    许多编程语言通过编译期的类型检查来验证开发者是否正确使用某个API,从而消除bug,但是python是动态的,没有提供编译器安全机制。python提供typeing模块给变量,函数,类中的字段,方法添加类型信息。这些类型提示信息可以实现渐进的类型判定机制,让我们在开发项目的过程中,把能够在编译期明确指定类型的地方逐渐确定下来。
    typing是进行类型注解,他可以搭配其他工具图mypy,pytype,pyright,pyre等工具进行静态分析,从而分析定义的类型,函数等是否得到正确使用。
    typing可以给泛型做注解,下面这种情况,输入可以是int或float但是除此之外的就不行了。
    image.png
    对于提前引用的情况下,可以将类写成字符串,这里如果直接写成SecondClass那么就会出现问题,因为这个类还没有定义呢,可以写成字符串的形式。python3.7中引入的方式,可以忽略提前引用,即可以直接写成value: SecondClass

    1. from __future__ import annotations

    image.png

  • 代码写完,测试完再写类型注解

  • 一般写在项目衔接处,即别人要调用的函数,方法里面
  • 复杂函数,方法