原文引至:
对于python中的模块和包, 我简直就想说, js nmlgb 就是一个 trash... 在前端写js根本就没有什么模块和包, 全部都是全局... 真lj...畅快了. 写了这么久的js, 连内部的模块的包都搞的这么复杂... 艹...
在python中, 已经定义好了完美的模块和包的引用机制.我们先来看看ModuleModule
在python中, 模块实际上,就相当于与业务逻辑解耦的可重用的一些函数方法. 所以, 我们可以预先定义一个模块:
// 命令为sam.pydef add(a,b): return a+b
之后在同目录中,打开一个 Terminator.
接着输入:>>> import sam>>> sam.add(1,2)//结果为:3
实际上, 这就是一个简单的模块. 但,当我们导入的时候, 会在Module的目录中生成一个.pyc文件, 该其实就是用来对Module进行缓存,并编译为Binary 文件, 以便py以后再次引入.
现在, 我们已经学会如何写入Module,引入Module. 这里,我们需要更进一步去探索, python是如何进行Module Path的搜索的.模块路径搜索
在nodeJS中, 他的搜索方式是, 先看内部定义的Module 有没有. 没有的话,则会开始寻找每一层的node_modules. 然后在里面搜索你的文件.js
那么在py中,他是怎样一个过程呢?py将搜索路径,放在了sys.path中.我们可以查看一下. sys.path里面的内容.import syssys.path
然后就会出现, 一些路径,比如这个:
['','C:\\Python33\\Lib\\lib-dynload','C:\\Windows\\system32\\python33.zip','C:\\Python33\\DLLs','C:\\Python33\\lib','C:\\Python33\\plat-darwin','C:\\Python33\\lib\\site-packages']
可以看到, 第一个为空, 他实际代表的就是current directory.
具体的顺序是:当前目录
sys.path定义的相关目录
安装依赖的目录
所以, 如果你想自定义自己的Module 直接添加在sys.path里面即可.
那应该怎样进行添加呢?实际上, 这就是查找关于sys.path的方法了. 还记得help()吗?查找后,我们基本上都会明白了. md, 这不就是list吗? 那剩下的不就是调用,list的方法进行添加和删除吗?一般而言,python的工作目录是放在对应的pythonLib文件夹内.这里,我将我常用的py 模块路径添加进去sys.path.append('/Users/jimmy_thr/Documents/pythonLib')
之后, 我只要将我写好的Module 放在指定的文件内即可.
模块进阶
上面, 我们只是学会使用基本的Module 导入, 实际上,py提供了 更加丰富的Module statement.
比如, 重命名, 指定导入, 全导入Module rename
这个算是一个py Module的一个附加值吧.
具体用法很简单.import sam as sbsb.add(1,2)
就酱, 实际上,就是使用as
将模块的名字换一个. 并且, 换了之后sam, 也就不存在了.
Specific Module
直接看demo吧:
//直接导入 add方法from sam import add// 也可以导入多个from sam import add,minus
这样导入的结果也是, 不能使用sam.
如果你想, 将module里面所有的方法都导入的话, 直接使用*
即可 from sam import *
不过, 真强烈不建议这么做. 因为, 并没有什么卵用, 并且, 万一出现什么bug, 都不知道这个方法哪来的.
Module complement
在nodeJS中, 他的模块引用是值引用类型, 即就是, 一次引用之后, 就会放在缓存里面, 以后如果在引用的话, 会直接从缓存里面取了.
看一个demo:def add(a,b): return a+bprint(add(1,2))//接着,我多次引用>>> import sam3>>> import sam
只会出现一次, 说明, sam Module 只能导入一次. 其余的就 nonesense. 但是,如果你的模块是内部循环型的, 那这样不就go die了吗?
hehe ~ py 早教看到这一点了, 在内置的中提供了reload方法, 来帮助我们实现, 模块的引用更新>>> import imp>>> imp.reload(sam)3
最后在补充一个dir(module)方法. 他的作用,就是用来查看指定Module当中的全局变量和函数名.
>>> dir(sam)['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add']
其余的用underscore来连接的, 是py中 module的一部分. 比较重要的看一下__name__即可. 他是用来表示,你当前所在的程序是模块,还是主程序.
如果你是主程序则 __name__ 为 main
如果为模块则 __name__ 为 你的模块名
另外,如果你忽略参数, 直接使用dir()的话,就是用来查看当前 全局中的变量和函数名.
>>> dir()['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '__warningregistry__', 'get', 'imp', 'sam', 'set']
但,如果你导入一个内置模块的话, 比如: copy
>>> import copy>>> dir(copy)['Error', 'PyStringMap', '_EmptyClass', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', ...]
仔细观察, 里面的内置属性, 会看到多出一个叫__all__
的属性. 这其实和下面所说的包有一些关系. 实际上, __all__ 的作用, 是提供公共接口的, 但实际上, 当我们导入方法的时候, 往往会全部倒入.比如这样.
import copy
那么此时, __all__ 对于这种方法是没有任何作用, 该语句就是用来导入copy下所有的全局变量.
现在, 假设 当前模块sam.py下有2个方法,add,minus.我将__all__ 设置为__all__ = ['add']
我们来试验一下两者的区别:
//首先导入:>>> import sam>>> dir()['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sam']>>> dir(sam)['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add', 'minus']
可以看到,有了sam这个模块名. 并且, 查看sam中,会看到有add和minus方法.
然后,我们换一种方式导入:>>> from sam import *>>> dir()['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'add']
我们会发现, minus不见了.
所以, 这里我们就可以搞懂了__all__ 到底是用来做什么的.__all__: 用来设置模块的公共接口. 并且只针对于
from module import *
的语句
最后在说一下,其中的另外两个默认属性 __doc__
,__file__
__doc__: 实际上,就是我们使用help()函数查看的内容. 内容具体就是我们写在模块开头或者函数开头的注释
__file__: 模块真实的文件路径, 如果你想查看源码的时候就很重要了.
放图:
Package
说完了,py中的Module, 接着就是最让你66的包. Module只是帮助我们完成了一个组件的一些小功能, 但是如果我们想要写一个可以调用的整体组件的话, 那么一个single Module显然是不够了. 所以,py 推出了package 来帮助我们完成这个巨大的工程(project). 我们可以通过个文件,调用,该组件下所有的方法. 比如,我们需要写一个html的评论框, 那么里面肯定会设计, HTML,css,js样式设计, 接收评论,发布评论等很多功能. 在包里面, 我们就可以把这些小功能进行拆分,达到复用的效果.
先看张图:这实际上, 就是我们python包的简单格式, 在每个文件根目录都会存在__init.py__ 文件. 他的作用实际上,就是用来定义, 引用包时, 暴露的相关接口. 而关键的关键, 就是上面提到的__all__ 内置的默认关键字. how to use?请, . __init__.py就是一个导入文件现在,我们来写一个简单的包, 以上图为例。在Game的根目录下, __init__.py内容为:__all__ = ['Sound','Image','Level']from Game import Sound,Image,Level
然后,我们就可以直接应用Game 包了.
>>>import Game>>>Game.Sound.xxx
这里,需要说明的是, 关于包的导入, 其实用不用all不是很重要, 换句话说, 应该是不推荐, 因为前文我们已经了解到, __all__ 生效的机制是 使用 from xx import *
这样的语句. 而这样做的实际效果是, 完全破坏了python的namespace机制, 也是编程语言中最重要的一个. 所以, 给的建议就是, 尽量放弃all的使用, 直接使用 import 来判断你需要导出那些公共的接口即可.
Leaving an __init__.py file empty is considered normal and even a good practice, if the package’s modules and sub-packages do not need to share any code.
出自: