模块

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在python中,一个.py文件就称之为一个模块(Module)

模块分为三种:

  • 自定义模块
  • 内置模块
  • 开源模块

PyPI

https://pypi.python.org/pypi

使用模块的好处

  1. 提高了代码的可维护性,可重用性.
  2. 避免函数名和变量名冲突,相同的名字的函数和变量完全可以分别存在不同的模块中,我们在编写模块时,不必考虑名字会与其他模块冲突.但要注意,尽量不要与内置函数名子冲突

python 内置函数

同时为了避免模块名冲突,python引入了按目录来组织模块的方法,称为包(Package)

比如: 一个abc.py的文件就是一个叫abc的模块和一个xyz.py的文件就是一个叫xyz的模块.现在abcxyz两个模块的名字与其他模块冲突,于是我们可以通过包来组织模块,为了避免冲突,方法就是选择一个顶层包名,比如mycompany,如下:

python-package-1

python-package-1

引入包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突.现在abc.py模块的名字就变成了mycompany.abc.

注意: 每一个包目录下面都会有一个__init__.py文件,这个文件是必须存在的,否则,python就把这个目录当成普通目录,而不是一个包,__init__.py文件可以是空文件,也可以有python代码,因为__init__.py本身就是一个模块,而它的模块名就是mycompany

类似的,可以有多及目录,组成多级层次的包结构.

python-package-2

python-package-2

web目录下的utils.py的模块名就是mycompany.web.utils,两个utils.py文件的模块名分别是mycompany.utilsmycompany.web.utils

自己创建模块时要注意命名,不能和python自带的模块名称冲突.

mycompany.web也是一个模块,其对应的文件为web目录下的__init__.py文件

导入模块

  1. import module
  2. from module.xx.xx import xx
  3. from module.xx.xx import xx as rename
  4. 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()时,才能打印出内容.

python-module-1

python-module-1

动态导入模块

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,等等。

内置模块

os

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()

re 正则

random 随机数