一个python代码文件,就是一个模块。
文件名就是模块名,在模块内部,全局变量name获取。
目的是为了代码的组织和复用。

每个模块都有自己的符号表,存储模块中定义函数、全局变量的名字,通过名字就可以访问对应的函数、全局变量,类似Lua的环境表。

导入模块

模块可以导入其它模块。被导入的模块名存放在调入模块的全局符号表中。
处于效率考虑,重复import只会执行一次,可以通过importlib.reload重载模块,见下面介绍。

  1. import module1[, module2[,... moduleN]]
  2. # 比如import module1
  3. # 将module1模块名存在当前模块的符号表中,通过module1就可获得module1的模块符号表
  4. # 通过符号表就可访问其内部定义的函数、全局变量
  5. # module1:只能是包、模块
  6. # import a.b.c.d时,a、b、c必须是包,d可以是模块/包,d不可以是c中定义的名字。
  7. #
  8. import module1 as newmodname
  9. # 效果同上,只是将新名字newmodname绑定到模块上。
  10. from modname import name1[, name2[, ... nameN]]
  11. # 将模块modname内的名字导入到当前模块的符号表中,类似C++ using std::cout的效果
  12. # 通过名字可直接访问到模块monname中对应定义的函数、全局变量。
  13. # modname:可能是包、模块
  14. # nameN: 可能是子模块,也可能是模块中定义的名字(函数、变量、模块)
  15. # from a.b.c import d,则可直接通过d来访问,不需要麻烦的a.b.c.d
  16. from modname import name1 as newname
  17. # 效果同上,只是将新名字绑定到name1绑定的内容上。
  18. from modname import *
  19. # 将模块modname内定义的所有名字导入到当前模块的符号表中,类似C++ using namespace::std的效果。
  20. # _下划线开头的名字也会被导入。
  21. # 不建议这么做,极易导致符号表名字冲突而发生覆盖。
  1. #********************fucker.py
  2. def shit():
  3. print("shit")
  4. shit();
  5. #********************test.py
  6. import fucker
  7. fucker.shit()
  8. from fucker import shit
  9. shit()

模块?脚本?

一个python代码文件既可以看成模块,也可以看成脚本。
当在代码中import时,这个文件被当做模块。
当通过命令行执行文件时,它是一个脚本。
这有什么意义?见如下代码:

  1. #############test.py
  2. def fuck(*args):
  3. for arg in args:
  4. print(arg)
  5. #加上下面这段逻辑,则test.py即可当脚本也可以当模块使用。
  6. if __name__ == "__main__": #当做脚本使用时,__name__ = __main__
  7. #当做模块使用时,__name__ = test
  8. import sys
  9. fuck(*sys.argv)
  10. #############在run.py中,被当做模块使用
  11. from test import fuck
  12. fuck(1, 2, 3, 4)
  13. #############在window cmd中,被当做脚本使用
  14. >> python test.py 1 2 3 4
  15. << test.py
  16. << 1
  17. << 2
  18. << 3
  19. << 4

模块搜索路径

import test,解释器查找test模块过程如下:

  1. 在内置模块中查找
  2. 在sys.path变量中查找test.py,它是一个字符串列表,初始值如下:
    1. 输入脚本目录(或者未指定文件时的当前目录),即正在运行脚本的目录。
    2. 环境变量PYTHONPATH中的每个目录
      1. windows:举个例子,PYTHONPATH=c:\python27\lib;
      2. UNIX:举个例子,PYTHONPATH=/usr/local/lib/python
    3. 默认路径
      1. 安装python时设定
      2. UNIX下,默认是/usr/local/lib/python/。

我们可以修改sys.path变量,来改变搜索路径。

  1. import sys
  2. sys.path.append('/ufs/guido/lib/python')

列出模块内名字:dir

  1. sortedlst = dir(module=None)
  2. # 返回module中定义的所有名字(函数、模块、变量),返回的是一个排好序的列表。
  3. # 不会返回内置的名字。
  4. # 默认返回当前模块的名字,
  5. import math
  6. content = dir(math)
  7. cur = dir()
  8. print content;
  9. ['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan',
  10. 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp',
  11. 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
  12. 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh',
  13. 'sqrt', 'tan', 'tanh']
  14. import builtins
  15. dir(builtins) #列出内置的名字。

缓存模块:pycache

为了加快模块载入,python在pycache 目录里缓存了每个模块的编译后版本,名称为 module.version.pyc ,

  • verson
    • 版本字段,它一般使用Python版本号。例如,在CPython版本3.3中,spam.py的编译版本将被缓存为 pycache/spam.cpython-33.pyc。此命名约定允许来自不同发行版和不同版本的Python的已编译模块共存。

python根据源文件的修改日期,和pyc编译版本比对来决定是否需要重新编译。pyc文件时平台无关的,可以不同平台共享。
Python在两种情况下不会检查缓存。首先,对于从命令行直接载入的模块,它从来都是重新编译并且不存储编译结果;其次,如果没有源模块,它不会检查缓存。为了支持无源文件(仅编译)发行版本, 编译模块必须是在源目录下,并且绝对不能有源模块。

  • 小建议
    • python命令中使用-0、-00可以减小编译文件大小。
      • -0:去除断言语句
      • -00:去除断言+doc文档字符串
    • pyc相比py源文件,唯一的优势就是载入速度更快,运行速度并没有更快
    • compileall模块可以编译目录下所有文件。

      重载模块:reload

      类似lua,重复导入,模块顶层代码只会执行一次。
      python2中,reload是内置函数。
      python3中,在importlib模块中。 ```python
      #######fuck1.py
      def fuck1(): print(“fuck1”)

fuck1() #顶层代码

#######shit.py

from importlib import reload import fuck1 #输出:fuck1 import fuck1 #没有输出

reload(fuck1) #输出:fuck1

  1. <a name="4aDpr"></a>
  2. # 包(Package)
  3. 包是python的一种机制,用来构造模块的命名空间,你可以类比C++的namespace。<br />不同的命名空间内的名字可以一样。<br />必须要包含有__init__.py文件的目录,才能被当作包,这个文件可以看成包的初始化文件。可以在里面执行初始化逻辑。<br />所以可以把目录看成包,把代码文件看成模块。
  4. ```python
  5. #在C++中
  6. # A::B,A、B都是命名空间,表示A中的B命名空间。
  7. # using A::B,就可以直接使用B中定义的名字了。
  8. #在python中
  9. # A.B,表示A包中的B模块。
  10. # import A.B,导入了A包中的B模块。
  1. '''
  2. 目录结构如下:
  3. test.py
  4. test2.py
  5. dir1/
  6. __init__.py
  7. fuck11.py 里面有一个fuck11函数,打印fuck11
  8. fuck12.py 里面有一个fuck12函数,打印fuck12
  9. dir2/ 子包
  10. __init__.py
  11. fuck21.py 里面有一个fuck21函数,打印fuck21
  12. fuck21.py 里面有一个fuck22函数,打印fuck22
  13. dir3/ 子包
  14. __init__.py
  15. fuck31.py 里面有一个fuck31函数,打印fuck31
  16. fuck32.py 里面有一个fuck32函数,打印fuck32
  17. '''
  18. ###############在test.py文件中,绝对导入,
  19. from dir1.fuck11 import fuck11
  20. from dir1.dir2.fuck21 import fuck21
  21. fuck11() #输出fuck11
  22. fuck21() #输出fuck21
  23. ###############在fuck31.py中,相对导入
  24. from ..dir2.fuck21 import fuck21 #导入fuck21函数
  25. from .. import dir2 #导入父目录下的dir2包
  26. from . import fuck31 #导入同目录下的fuck31模块
  27. #在主模块中,必须使用绝对导入,因为相对导入是基于当前模块的名称导入的
  28. #而主模块的名字一直是"__main__"。
  29. #主模块就是那个从命令行启动的模块,也就是以脚本启动的模块,一般是程序的入口。
  30. ###############test2.py
  31. #在dir1/__init__.py中代码如下:
  32. __all__ = [ #__all__变量,包含当前所在包的所有子部分(子包、子模块、定义的名字)
  33. "fuck11" #决定了import *的导入内容。
  34. ]
  35. from dir1 import * #只会导入fuck11模块。