模块
函数可以复用代码。如果想复用一系列的函数怎么办?如你所想,答案就是模块。
有很多写模块的方法,但是最简单的还是创建一个后缀为.py
的文件,然后在里面写上函数和变量。
另一种写模块的方法:用本地语言(用来写Python解释器的语言)写模块。比如说,你可以用c语言来写模块,经过编译后,你就可以通过Python解释器使用它了。
其它程序可以导入模块,然后使用模块里的功能。就如我们使用Python的标准库一样。首先,我们学习如何使用标准库里的模块。
例子(保存至module_using_sys.py
):
import sys
print('命令行参数:')
for arg in sys.argv:
print(arg)
print('PYTHON_PATH:', sys.path)
输出:
$ python module_using_sys.py we are arguments
命令行参数:
module_using_sys.py
we
are
arguments
PYTHON_PATH: [.......]
它是怎么运作的呢?
我们先是用import语句导入sys模块,告诉Python,我们想要使用这个模块。sys模块包含关于Python解释器和Python环境的功能。
当Python执行import sys
时,先是寻找sys模块。因为sys模块是内置的,因此Python肯定知道在哪里能够找到它。
如果不是编译好的模块,而是用Python写的模块呢?Python解释器会搜索一些目录,去找要导入的模块。这些目录就是sys.path的列表。如果模块找到了,模块里的代码就会被执行,此刻开始,模块就生效了。注意,只有第一次导入一个模块的时候,才会有上述的初始化过程。
sys模块里的argv变量,可以用.
后缀获取到,也就是sys.argv。用这种点号后缀方式,清晰地表现出变量argv属于sys模块。这种方式另外一个优点就是程序里的名字不会发生冲突。
sys.argv变量表示一个字符串列表。进一步讲,sys.argv包含命令行的参数值,也就是,在输入执行程序的命令时,传入的参数值。
如果你正在用IDE写程序,运行程序,在菜单栏找到可以输入命令行参数的选项。
当执行python module_using_sys.py we are arguments
时,只有python是命令,其它都是参数值,它们将会被保存到sys.argv变量里。
记住,运行的脚本名字是参数值列表的第一个元素。所以在本例中,sys.argv[0]可以表示module_using_sys.py
。sys.argv[1]表示we
,sys.argv[2]表示are
,sys.argv[3]表示arguments
。注意,Python从0开始计数。
sys.path包含一个目录列表,这些目录用来放置模块。可以看到,sys.path的列表的第一个元素是一个空字符串,它用来表示当前目录。也就是说,你可以直接导入当前目录里的模块。其他情况下,你需要把模块放到sys.path列表中目录的一个。
注意,当前目录就是执行程序时用户所在的目录。你可以导入os模块,然后输入print(os.getcwd())
来找出当前目录。
byte-compiled .pyc文件
导入模块的开销是挺大的,因此Python有一些技巧可以让它更快。创建byte-compiled
文件(后缀为.pyc
)就是其中之一。byte-compiled
文件是Python执行程序时产生的中间代码。这种文件在多个程序导入相同的模块时很有用,因为导入模块的一些处理过程被跳过了。同样,这些文件各个平台都不一样。
注意:这些.pyc
文件一般生成在对应.py
文件所在的目录。如果Python在该目录没有写的权限,那么.pyc
文件无法被创建。
from...import语句
如果想直接导入argv变量到你的程序里(不用每次都在前面加上sys.
),你可以使用from sys import argv
语句。
警告:
一般要避免使用from ... import
语句,因为它会增加名冲突的倾向,而且让程序更难阅读。
例子
from math import sqrt
print('16的平方根', sqrt(16))
模块属性name
每个模块都有一个名字。在一个模块里,可以用语句去找到所用模块的名字。这个在查看模块是作为脚本在运行,还是被导入时相当有用。当一个模块初次被导入,模块内的代码被执行。模块作为脚本运行和被导入的表现会有所差异。模块的name会有所不同,就是其中之一。
例子(保存至module_using_name.py
):
if __name__ == '__main__':
print('独自运行')
else:
print('被导入')
输出:
$ python module_using_name.py
独自运行
$ python
>>> import module_using_name
被导入
它是怎么运作的呢?
每一个模块都会定义name变量。如果它被赋值__main__
,就表示模块作为脚本在运行,这个时候你就可以搞点事了。
创建模块
创建模块很简单,你一直都在创建模块。因为,在Python中每一个程序都是一个模块。你只要让Python程序带有.py
后缀就可以了。
例子(保存至mymodule.py
):
def say_hi():
print('哎,这就是模块?')
__version__ = '0.1'
这是一个模块样例。所以,模块和我们通常写的程序并没有什么不同。下面将讲解怎么在程序中使用模块。
记住,模块应该放在程序运行的目录,或者sys.path列表中的某个目录。
另外一个例子(保存至mymodule_demo.py
):
import mymodule
mymodule.say_hi()
print('版本', mymodule.__version__)
输出:
$ python mymodule_demo.py
哎,这就是模块?
版本 0.1
它是怎么运作的呢?
注意,我们使用点号去获取模块的成员。Python会在相似的场合下复用某种符号,所以你不用去学习新的方式去完成任务,这种风格被称为pythonic
。
下面是使用from ...import
的版本(保存至mymodule_demo2.py
):
from mymodule import say_hi, __version__
say_hi()
print('版本', __version__)
输出与运行mymodule_demo.py
的一样。
注意,假设某个模块有version名,如果再用from...import导入version会发生名冲突。这种情况很有可能发生,因为通常都是用version用作版本号。因此,推荐使用import语句而不是使用from...import。
你也可以这样写:
from mymodule import *
这种情况会导入所有的公共名,但是以双下滑线开始的名不会被导入。
警告:
你应该避免使用import *。
Python的禅:
一个Python的准则是:明确的比隐晦的好。
dir函数
内置的dir函数返回一个对象的名列表。如果对象是一个模块,这个列表会包含在其中定义的函数,类和变量。
这个函数接受参数值。如果参数是一个模块的名,函数会返回一个模块内名的列表。如果没有传如参数值,函数返回当前模块的名列表。
例子:
$ python
>>> import sys
>>> dir(sys)
['__displayhook__',
'__doc__',
...,
]
>>> dir()
['__builtins__',
'__doc__',
...,
]
>>> a = 5
>>> dir()
['__builtins__',
'__doc__',
'sys',
'a',
...,
]
>>> del a
>>> dir()
['__builtins__',
'__doc__',
...,
]
它是怎么运作的呢?
我们先是向dir传入sys名。可以看到sys模块包含了很多属性。
接下来,不给dir传参数值。默认情况,它会返回当前模块的所有属性。注意,导入的模块也会出现在列表里。
为了观察dir的特性,我们定义了一个变量,并给它赋值,然后查看dir的结果,发现新增了一个同变量同名的元素。当使用del把变量从当前模块移除后,dir打印出的结果中发生了相似的现象。
del的小提示:该语句用来删除变量或名,在语句被执行后,例子中的a变量被删除了。你不再能访问a变量了,就好象之前没有存在过。
注意,dir函数可以传入任意对象名。举个栗子,执行dir(str)
会返回str类的所有属性。
有一个vars()
函数,会给出对象的属性和它的值,但是它在某些情况下不起效。
--
包
--
你肯定观察到了程序是有层次的。变量一般在函数里定义。函数和全局变量一般定义在模块里。那么怎么组织模块呢?答案就是包。
包就是包含模块的文件夹,但是它会包含一个特殊的文件 - __init__.py
。这个文件用来告知Python,该文件夹是一个包。
下面开始创建一个叫做world
的包,在里面包含asia
包、africa
包等等。这些子包包含一些模块,像india
、madagascar
等等。
下面是目录结构:
world/
__init__.py
asia/
__init__.py
india/
__init__.py
foo.py
africa/
__init__.py
madagascar/
__init__.py
bar.py
用包来组织模块很便捷。你在标准库里可以看到很多例子。
总结
待续。。。