模块¶
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在python中,一个.py文件就称之为一个模块(Module)
模块分为三种:
- 自定义模块
- 内置模块
- 开源模块
PyPI
使用模块的好处¶
- 提高了代码的可维护性,可重用性.
- 避免函数名和变量名冲突,相同的名字的函数和变量完全可以分别存在不同的模块中,我们在编写模块时,不必考虑名字会与其他模块冲突.但要注意,尽量不要与内置函数名子冲突
包¶
同时为了避免模块名冲突,python引入了按目录来组织模块的方法,称为包(Package)
比如:
一个abc.py
的文件就是一个叫abc
的模块和一个xyz.py
的文件就是一个叫xyz
的模块.现在abc
和xyz
两个模块的名字与其他模块冲突,于是我们可以通过包来组织模块,为了避免冲突,方法就是选择一个顶层包名,比如mycompany
,如下:
引入包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突.现在abc.py
模块的名字就变成了mycompany.abc
.
注意:
每一个包目录下面都会有一个__init__.py
文件,这个文件是必须存在的,否则,python就把这个目录当成普通目录,而不是一个包,__init__.py
文件可以是空文件,也可以有python代码,因为__init__.py
本身就是一个模块,而它的模块名就是mycompany
类似的,可以有多及目录,组成多级层次的包结构.
web
目录下的utils.py
的模块名就是mycompany.web.utils
,两个utils.py
文件的模块名分别是mycompany.utils
和mycompany.web.utils
自己创建模块时要注意命名,不能和python自带的模块名称冲突.
mycompany.web
也是一个模块,其对应的文件为web目录下的__init__.py文件
导入模块¶
- import module
- from module.xx.xx import xx
- from module.xx.xx import xx as rename
- from module.xx.xx import *
导入模块就是告诉python解释器去解释哪个py
- 导入一个py文件,解释器解释该py文件
- 导入一个包,解释器解释该包下的
__init__.py
导入模块时,根据下面路径作为基准来进行,sys.path
>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages']
当我们试图加载一个模块时,Python会在上述路径下搜索对应的.py文件,如果找不到,就会报错:
>>> import mymodule
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named mymodule
如果sys.path路径列表没有想要的路径,可以通过sys.path.append(‘路径’)添加
>>> import sys
>>> import os
>>> pre_path = os.path.abspath('../')
>>> sys.path.append(pre_path)
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages', '/Users']
示例代码:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# 模块的注释文档,任何模块代码的第一个字符串都被视为模块的文档注释
' a test module'
# 使用__author__变量把作者写进去
__author__ = 'Michael'
# 导入模块
import sys
def test():
args = sys.argv
if len(args) == 1:
print("Hello,World")
elif len(args) == 2:
print('Hello, %s! ' % args[1])
else:
print("Too many arguments!")
if __name__ == '__main__':
test()
比如,使用sys
模块,第一步,导入模块
import sys
导入sys
模块后,就有sys
变量指向该模块,利用sys
这个变量,就可以访问sys
模块的所有功能.
sys
模块有一个argv
变量,用list存储了命令行的所有参数.argv
至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:
运行python3 hello.py
获得的sys.argv
就是[hello.py]
运行python3 hello.py Michael
获得的sys.argv
就是['hello.py','Michael']
最后两行代码:
if __name__ == '__main__':
test()
当我们在命令行运行hello
模块文件时,python解释器把一个特殊变量__name__
置为__main__
,而如果在其他地方导入该hello
模块时,if
判断将失败,因此,这种if
测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试
.
在python交互环境,再导入hello
模块,导入时,没有打印内容,因为没有执行
test()
函数,调用hello.test()
时,才能打印出内容.
动态导入模块¶
import importlib
__import__('import_lib.metaclass') # 这是解释器自己内部使用的
# importlib.import_module('import_lib.metaclass') # 与上面这句效果一样, 官方建议用这个
作用域¶
在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人用,有的函数和变量我们希望仅仅在模块内部使用.在python中,是通过_
前缀来实现的.
正常的函数和变量名是公开的(public),可以直接被引用,比如:abc
,xyz
等
类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊变量,hello
模块定义的文档注释也可以用特殊变量__doc__
访问,我们自己的变量一般不要用这种变量名
类似_xxx
和__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用
之所以说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量.
private函数或变量不应该被别人引用.它们的用处:
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
安装开源模块¶
安装方法有两种
- 一
- yum
- pip
- apt-get
- …
- 二
- 下载源码
- 解压源码
- 进入目录
- 编译源码
- 安装源码
使用源码安装,需要使用到gcc和python开发环境
需要先执行:
yum install gcc python-devel
或者
apt-get python-dev
安装成功后,模块会自动添加到sys.path中的某个目录中,比如
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages
如果你正在使用Windows,确保安装时勾选了pip和Add python.exe to Path。
在命令提示符窗口下尝试运行pip,如果Windows提示未找到命令,可以重新运行安装程序添加pip。
注意:Mac或Linux上有可能并存Python 3.x和Python 2.x,因此对应的pip命令是pip3。
一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Pillow的名称叫Pillow,因此,安装Pillow的命令就是:
pip install Pillow
耐心等待下载并安装后,就可以使用Pillow了。
有了Pillow,处理图片易如反掌。随便找个图片生成缩略图:
>>> from PIL import Image
>>> im = Image.open('test.png')
>>> print(im.format, im.size, im.mode)
PNG (400, 300) RGB
>>> im.thumbnail((200, 100))
>>> im.save('thumb.jpg', 'JPEG')
其他常用的第三方库还有MySQL的驱动:mysql-connector-python
,用于科学计算的NumPy库:numpy
,用于生成文本的模板工具Jinja2
,等等。
内置模块¶
sys¶
用于提供对解释器相关的操作
sys.argv 命令行参数`列表`,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdout.write('please:')
val = sys.stdin.readline()[:-1] # 从标准输入读入
subprocess 执行系统命令¶
可以执行shell命令的相关模块和函数有:
- os.system
- os.spawn*
- os.popen* # 废弃
- popen2.* # 废弃
- commands.* # 废弃,3.x中被移除
以上执行shell命令的相关的模块和函数的功能均在 subprocess 模块中实现,并提供了更丰富的功能。
call¶
执行命令,返回状态码
>>> import subprocess
>>> ret = subprocess.call(["ls","-l"],shell=False)
# shell = True,允许shell命令是字符串形式
>>> ret = subprocess.call(["ls -l"],shell=True)
check_call¶
执行命令,如果执行状态是0,则返回0,否则抛出异常
>>> subprocess.check_call(["ls","-l"])
>>> subprocess.check_call("exit 1",shell=True)
check_output¶
执行命令,如果状态码是0,则返回执行结果,否则抛出异常
>>> subprocess.check_output(["echo","Hello World!"])
b'Hello World!\n'
>>> subprocess.check_output("exit 1",shell=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 316, in check_output
**kwargs).stdout
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 398, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
>>>
subprocess.Popen(…)¶
用于执行复杂的系统命令
参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
- universal_newlines:不同系统的换行符不同,True -> 同意使用
\n
- startupinfo与createionflags只在windows下有效
- 将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等
执行普通命令
>>> import subprocess
>>> ret1 = subprocess.Popen(["mkdir","t1"])
>>> ret2 = subprocess.Popen("mkdir t2",shell=True)
终端输入的命令分为两种
- 输入即可得到输出,如ifconfing
- 输入进行某环境,依赖再输入,如python
shutil¶
高级的 文件,文件夹,压缩包处理模块
shutil.copyfileobj(fsrc, fdst[, length])¶
将文件内容拷贝到另一个文件中,可以部分内容
def copyfileobj(fsrc, fdst, length=16*1024):
"""copy data from file-like object fsrc to file-like object fdst"""
while 1:
buf = fsrc.read(length)
if not buf:
break
fdst.write(buf)
shutil.copyfile(src, dst)¶
拷贝文件
def copyfile(src, dst):
"""Copy data from src to dst"""
if _samefile(src, dst):
raise Error("`%s` and `%s` are the same file" % (src, dst))
for fn in [src, dst]:
try:
st = os.stat(fn)
except OSError:
# File most likely does not exist
pass
else:
# XXX What about other special files? (sockets, devices...)
if stat.S_ISFIFO(st.st_mode):
raise SpecialFileError("`%s` is a named pipe" % fn)
with open(src, 'rb') as fsrc:
with open(dst, 'wb') as fdst:
copyfileobj(fsrc, fdst)
shutil.copymode(src, dst)¶
仅拷贝权限。内容、组、用户均不变
def copymode(src, dst):
"""Copy mode bits from src to dst"""
if hasattr(os, 'chmod'):
st = os.stat(src)
mode = stat.S_IMODE(st.st_mode)
os.chmod(dst, mode)
shutil.copystat(src, dst)¶
拷贝状态的信息,包括:mode bits, atime, mtime, flags
def copystat(src, dst):
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
st = os.stat(src)
mode = stat.S_IMODE(st.st_mode)
if hasattr(os, 'utime'):
os.utime(dst, (st.st_atime, st.st_mtime))
if hasattr(os, 'chmod'):
os.chmod(dst, mode)
if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
try:
os.chflags(dst, st.st_flags)
except OSError, why:
for err in 'EOPNOTSUPP', 'ENOTSUP':
if hasattr(errno, err) and why.errno == getattr(errno, err):
break
else:
raise
configparser 配置文件¶
用于对特定的配置进行操作,当前模块的名称在 python 3.x 版本中变更为 configparser
logging 日志¶
用于便捷记录日志且线程安全的模块
time & datetime 时间¶
时间相关的操作,时间有三种表示方式:
- 时间戳 1970年1月1日之后的秒,即:time.time()
- 格式化的字符串 2014-11-11 11:11, 即:time.strftime(‘%Y-%m-%d’)
- 结构化时间 元组包含了:年、日、星期等… time.struct_time 即:time.localtime()