# 模块化概述

# sys.path

  • 包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。

    • 比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。
    • 就好像使用模块的时候,不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。
  • 搜索路径是一个解释器会先进行搜索的所有目录的列表。当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

    • 搜索路径被存储在sys模块的path变量中(不是windows环境变量下的那个PATH)

      • Linux和Windows下都是PYTHONPATH​环境变量
      • 如果设置了PYTHONPATH​环境变量,则会覆盖掉默认搜索路径
    • path​输出是一个列表,由一系列目录名组成,第一项是一个空串表示当前目录。

    • sys.path.append("..")​ 表示将当前程序所在位置向上提了一级

  • 可以创建一个.pth​文件访问Python安装路径下的site-packages/sitepackages​目录,这样解释器启动时会自动将其中列举出的目录加入到sys.path​中

    # myapplication.pth
    /some/dir
    /other/dir
    
  • __init__​文件是Python2的要求,Python3并不强制要求

  • 一个模块只会被导入一次,不管你执行了多少次import​,这样可以防止导入模块被一遍又一遍地执行。

    • 使用reload方法重新导入

      import aaa
      from imp import reload
      reload(aaa)
      
  • import 在导入文件的时候,会自动把所有暴露在外面的代码全都执行一遍(第一次导入时)。因此,如果你要把一个东西封装成模块,又想让它可以执行的话,你必须将要执行的代码放在 if __name__ == '__main__'​​下面。

    • __name__​​作为Python 的魔术内置参数,本质上是模块对象的一个属性。我们使用 import 语句时,__name​​就会被赋值为该模块的名字,自然就不等于 __main__​​了。
    • 巧用if __name__== '__main__'​​来避开 import 时执行。
import sys
sys.path.append("..")

from utils.class_utils import *

encoder = Encoder()
decoder = Decoder()

print(encoder.encode('abcde'))
print(decoder.decode('edcba'))

########## 输出 ##########

edcba
abcde

# 项目模块化

  • 相对位置会使得重构既不雅观,也易出错。因此,在大型工程中尽可能使用绝对位置是第一要义。对于一个独立的项目,所有的模块的追寻方式,最好从项目的根目录开始追溯,这叫做相对的绝对路径。

    • 以项目的根目录作为最基本的目录,所有的模块调用,都要通过根目录一层层向下索引的方式来 import。

# 猴子补丁

“猴子补丁”是动态类型语言的一个特性,代码运行时在不修改源代码的前提下改变代码中的方法、属性、函数等以达到热补丁(hot patch)的效果。很多系统的安全补丁也是通过猴子补丁的方式来实现的,但实际开发中应该避免对猴子补丁的使用,以免造成代码行为不一致的问题。

Monkey-patching猴子补丁这个叫法源自于Zope框架,大家在修正Zope的Bug时经常会在程序后追加更新部分,这些被称作“杂牌军补丁(guerillapatch)”,后来guerilla​逐渐写成了gorllia​(猩猩),再后来就写成了monkey​(猴子),所以猴子补丁的叫法就这么莫名其妙的得来了。之后在动态语言中,不改变源代码而对功能进行追加和变更就统称为“猴子补丁”。所以猴子补丁并不是Python中专有的,猴子补丁充分利用了动态语言的灵活性,可以对现有语言API进行追加、替换、修改,甚至性能优化等。

在使用gevent​库的时候,我们会在代码开头的地方执行gevent.monkey.patch_all()​,这行代码的作用是把标准库中的socket​模块给替换掉,这样我们在使用socket​的时候,不用修改任何代码就可以实现对代码的协程化,达到提升性能的目的,这就是对猴子补丁的应用。

另外,如果希望用ujson​三方库替换掉标准库中的json​,也可以使用猴子补丁的方式,代码如下所示。

import json, ujson

json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads

单元测试中的Mock​技术也是对猴子补丁的应用,Python中的unittest.mock​模块就是解决单元测试中用Mock​对象替代被测对象所依赖的对象的模块。