zoukankan      html  css  js  c++  java
  • python 模块

    Python 模块

    模块让你能够有逻辑地组织你的Python代码段。

    把相关的代码分配到一个 模块里能让你的代码更好用,更易懂。

    模块也是Python对象,具有随机的名字属性用来绑定或引用。

    简单地说,模块就是一个保存了Python代码的文件。模块能定义函数,类和变量。模块里也能包含可执行的代码。

    为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。

    模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。

    模块分为三种:

    • 自定义模块
    • 第三方模块
    • 内置模块

    下面是一个使用 python 标准库中模块的例子。

    #!/usr/bin/python3
    # 文件名: using_sys.py
    
    import sys
    
    print('命令行参数如下:')
    for i in sys.argv:
       print(i)
    
    print('
    
    Python 路径为:', sys.path, '
    ')
    

     执行结果如下所示:

    $ python using_sys.py 参数1 参数2
    命令行参数如下:
    using_sys.py
    参数1
    参数2
    
    
    Python 路径为: ['/root', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages'] 
    
    • 1、import sys 引入 python 标准库中的 sys.py 模块;这是引入某一模块的方法。
    • 2、sys.argv 是一个包含命令行参数的列表。
    • 3、sys.path 包含了一个 Python 解释器自动查找所需模块的路径的列表。

    例子

    一个叫做aname的模块里的Python代码一般都能在一个叫aname.py的文件中找到。下例是个简单的模块support.py。

    def print_func( par ):
       print "Hello : ", par
       return
    

    import 语句

    想使用Python源文件,只需在另一个源文件里执行import语句,语法如下:

    import module1[, module2[,... moduleN]
    

    当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。

    一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。

    当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?

    这就涉及到Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。

    这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。

    搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量,做一个简单的实验,在交互式解释器中,输入以下代码:

    >>> import sys
    >>> sys.path
    ['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
    >>> 
    

    sys.path 输出是一个列表,其中第一项是空串'',代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),亦即我们执行python解释器的目录(对于脚本的话就是运行的脚本所在的目录)。

    因此若像我一样在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。

    了解了搜索路径的概念,就可以在脚本中修改sys.path来引入一些不在搜索路径中的模块。

    现在,在解释器的当前目录或者 sys.path 中的一个目录里面来创建一个fibo.py的文件,代码如下:

    # 斐波那契(fibonacci)数列模块
    
    def fib(n):    # 定义到 n 的斐波那契数列
        a, b = 0, 1
        while b < n:
            print(b, end=' ')
            a, b = b, a+b
        print()
    
    def fib2(n): # 返回到 n 的斐波那契数列
        result = []
        a, b = 0, 1
        while b < n:
            result.append(b)
            a, b = b, a+b
        return result
    

    然后进入Python解释器,使用下面的命令导入这个模块:

    >>> import fibo
    

    这样做并没有把直接定义在fibo中的函数名称写入到当前符号表里,只是把模块fibo的名字写到了那里。

    可以使用模块名称来访问函数:

    >>> fibo.fib(1000)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
    >>> fibo.fib2(100)
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    >>> fibo.__name__
    'fibo'
    

    如果你打算经常使用一个函数,你可以把它赋给一个本地的名称:

    >>> fib = fibo.fib
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

     调用多个模块的方法:

    #第一种(推荐)
    import os
    import sys
    
    #第二种
    import os,sys
    

    如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append('路径') 添加。

    1
    2
    3
    4
    import sys
    import os
    project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(project_path)

    From…import 语句

    导入模块其实就是告诉Python解释器去解释那个py文件

    • 导入一个py文件,解释器解释该py文件
    • 导入一个包,解释器解释该包下的 __init__.py 文件 【py2.7】

    Python的from语句让你从模块中导入一个指定的部分到当前命名空间中。语法如下:

    from modname import name1[, name2[, ... nameN]]
    

     例如,要导入模块fib的fibonacci函数,使用如下语句:

    from fib import fibonacci
    

    这个声明不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个引入到执行这个声明的模块的全局符号表。


    From…import* 语句

    把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

    from modname import *
    

    这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。


     

    深入模块

    模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。

    每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。

    所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞花。

    从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。

    模块是可以导入其他模块的。在一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,当然这只是一个惯例,而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。

    还有一种导入的方法,可以使用 import 直接把模块内(函数,变量的)名称导入到当前操作模块。比如:

    >>> from fibo import fib, fib2
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    
     

    这种导入的方法不会把被导入的模块的名称放在当前的字符表中(所以在这个例子里面,fibo 这个名称是没有定义的)。

    这还有一种方法,可以一次性的把模块中的所有(函数,变量)名称都导入到当前模块的字符表:

    >>> from fibo import *
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

    这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。


    __name__属性

    一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

    #!/usr/bin/python3
    # Filename: using_name.py
    
    if __name__ == '__main__':
       print('程序自身在运行')
    else:
       print('我来自另一模块')
    

    运行输出如下:

    $ python using_name.py
    

    程序自身在运行

    $ python
    >>> import using_name
    我来自另一模块
    >>>
    

    说明: 每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。


    dir() 函数

    内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回:
    </p>
    <pre>
    >>> import fibo, sys
    >>> dir(fibo)
    ['__name__', 'fib', 'fib2']
    >>> dir(sys)  
    ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
     '__package__', '__stderr__', '__stdin__', '__stdout__',
     '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
     '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
     'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
     'call_tracing', 'callstats', 'copyright', 'displayhook',
     'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
     'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
     'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
     'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
     'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
     'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
     'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
     'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
     'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
     'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
     'thread_info', 'version', 'version_info', 'warnoptions']
    

    如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:

    >>> a = [1, 2, 3, 4, 5]
    >>> import fibo
    >>> fib = fibo.fib
    >>> dir() # 得到一个当前模块中定义的属性列表
    ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
    >>> a = 5 # 建立一个新的变量 'a'
    >>> dir()
    ['__builtins__', '__doc__', '__name__', 'a', 'sys']
    >>>
    >>> del a # 删除变量名a
    >>>
    >>> dir()
    ['__builtins__', '__doc__', '__name__', 'sys']
    >>>
    

    标准模块

    Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。

    有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。

    这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。

    应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:

    >>> import sys
    >>> sys.ps1
    '>>> '
    >>> sys.ps2
    '... '
    >>> sys.ps1 = 'C> '
    C> print('Yuck!')
    Yuck!
    C>
    

    PYTHONPATH变量

    作为环境变量,PYTHONPATH由装在一个列表里的许多目录组成。PYTHONPATH的语法和shell变量PATH的一样。

    在Windows系统,典型的PYTHONPATH如下:

    set PYTHONPATH=c:python20lib;
    

     在UNIX系统,典型的PYTHONPATH如下:

    set PYTHONPATH=/usr/local/lib/python
    

    命名空间和作用域

    变量是拥有匹配对象的名字(标识符)。命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。

    一个Python表达式可以访问局部命名空间和全局命名空间里的变量。如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。

    每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。

    Python会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的。

    因此,如果要给全局变量在一个函数里赋值,必须使用global语句。

    global VarName的表达式会告诉Python, VarName是一个全局变量,这样Python就不会在局部命名空间里寻找这个变量了。

    例如,我们在全局命名空间里定义一个变量money。我们再在函数内给变量money赋值,然后Python会假定money是一个局部变量。然而,我们并没有在访问前声明一个局部变量money,结果就是会出现一个UnboundLocalError的错误。取消global语句的注释就能解决这个问题。

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    Money = 2000
    def AddMoney():
       # 想改正代码就取消以下注释:
       # global Money
       Money = Money + 1
     
    print Money
    AddMoney()
    print Money
    

    dir()函数

    dir()函数一个排好序的字符串列表,内容是一个模块里定义过的名字。

    返回的列表容纳了在一个模块里定义的所有模块,变量和函数。如下一个简单的实例:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    # 导入内置math模块
    import math
     
    content = dir(math)
     
    print content;
    

    以上实例输出结果:

    ['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan', 
    'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 
    'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
    'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 
    'sqrt', 'tan', 'tanh']
    

     在这里,特殊字符串变量__name__指向模块的名字,__file__指向该模块的导入文件名。

    globals()和locals()函数

    根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。

    如果在函数内部调用locals(),返回的是所有能在该函数里访问的命名。

    如果在函数内部调用globals(),返回的是所有在该函数里能访问的全局名字。

    两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。


    reload()函数

    当一个模块被导入到一个脚本,模块顶层部分的代码只会被执行一次。

    因此,如果你想重新执行模块里顶层部分的代码,可以用reload()函数。该函数会重新导入之前导入过的模块。语法如下:

    reload(module_name)
    

    在这里,module_name要直接放模块的名字,而不是一个字符串形式。比如想重载hello模块,如下:

    reload(hello)
    

    Python中的包

    包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的Python的应用环境。

    包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。

    比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

    就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

    这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。

    不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。

    现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。

    并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所你还需要一组怎么也写不完的模块来处理这些操作。

    这里给出了一种可能的包结构(在分层的文件系统中):

    sound/                          顶层包
          __init__.py               初始化 sound 包
          formats/                  文件格式转换子包
                  __init__.py
                  wavread.py
                  wavwrite.py
                  aiffread.py
                  aiffwrite.py
                  auread.py
                  auwrite.py
                  ...
          effects/                  声音效果子包
                  __init__.py
                  echo.py
                  surround.py
                  reverse.py
                  ...
          filters/                  filters 子包
                  __init__.py
                  equalizer.py
                  vocoder.py
                  karaoke.py
                  ...
    

    在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

    目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

    最简单的情况,放一个空的 :file:__init__.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。

    用户可以每次只导入一个包里面的特定模块,比如:

    import sound.effects.echo
    

    考虑一个在Phone目录下的pots.py文件。这个文件有如下源代码:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    def Pots():
       print "I'm Pots Phone"
    

    同样地,我们有另外两个保存了不同函数的文件:

    • Phone/Isdn.py 含有函数Isdn()
    • Phone/G3.py 含有函数G3()

    现在,在Phone目录下创建file __init__.py:

    • Phone/__init__.py

    当你导入Phone时,为了能够使用所有函数,你需要在__init__.py里使用显式的导入语句,如下:

    from Pots import Pots
    from Isdn import Isdn
    from G3 import G3
    

    当你把这些代码添加到__init__.py之后,导入Phone包的时候这些类就全都是可用的了。

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    # 导入 Phone 包
    import Phone
     
    Phone.Pots()
    Phone.Isdn()
    Phone.G3()
    

    以上实例输出结果:

    I'm Pots Phone
    I'm 3G Phone
    I'm ISDN Phone
    

    如上,为了举例,我们只在每个文件里放置了一个函数,但其实你可以放置许多函数。你也可以在这些文件里定义Python的类,然后为这些类建一个包。

    包内引用

    如果包中使用了子包结构(就像示例中的 sound 包),可以按绝对位置从相邻的包中引入子模块。例如,如果 sound.filters.vocoder 包需要使用 sound.effects 包中的 echo 模块,它可以 from sound.Effects import echo

    你可以用这样的形式 from module import name 来写显式的相对位置导入。那些显式相对导入用点号标明关联导入当前和上级包。以 surround 模块为例,你可以这样用:

    from . import echo
    from .. import formats
    from ..filters import equalizer
    

    需要注意的是显式或隐式相对位置导入都基于当前模块的命名。因为主模块的名字总是 "__main__",Python 应用程序的主模块应该总是用绝对导入。

    多重目录中的包

    包支持一个更为特殊的特性, __path__。 在包的 __init__.py 文件代码执行之前,该变量初始化一个目录名列表。该变量可以修改,它作用于包中的子包和模块的搜索功能。

    这个功能可以用于扩展包中的模块集,不过它不常用。

    从一个包中导入*

    设想一下,如果我们使用 from sound.effects import *会发生什么?

    Python 会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来。

    但是很不幸,这个方法在 Windows平台上工作的就不是非常好,因为Windows是一个大小写不区分的系统。

    在这类平台上,没有人敢担保一个叫做 ECHO.py 的文件导入为模块 echo 还是 Echo 甚至 ECHO。

    (例如,Windows 95就很讨厌的把每一个文件的首字母大写显示)而且 DOS 的 8+3 命名规则对长模块名称的处理会把问题搞得更纠结。

    为了解决这个问题,只能烦劳包作者提供一个精确的包的索引了。

    导入语句遵循如下规则:如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

    作为包的作者,可别忘了在更新包之后保证 __all__ 也更新了啊。你说我就不这么做,我就不使用导入*这种用法,好吧,没问题,谁让你是老板呢。这里有一个例子,在:file:sounds/effects/__init__.py中包含如下代码:

    __all__ = ["echo", "surround", "reverse"]
    

    这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。

    如果 __all__ 真的没有定义,那么使用from sound.effects import *这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。

    这会把 __init__.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:

    import sound.effects.echo
    import sound.effects.surround
    from sound.effects import *
    

    这个例子中,在执行from...import前,包sound.effects中的echo和surround模块都被导入到当前的命名空间中了。(当然如果定义了__all__就更没问题了)

    通常我们并不主张使用*这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。

    记住,使用from Package import specific_submodule这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。

    如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包sound.effects中的模块echo,你就要写成 from sound.effects import echo。

    from . import echo
    from .. import formats
    from ..filters import equalizer
    

    无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"__main__",一个Python应用程序的主模块,应当总是使用绝对路径引用。

    包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。

    这个功能并不常用,一般用来扩展包里面的模块。

    在Python中,安装第三方模块,是通过包管理工具pip完成的。

    如果你正在使用Mac或Linux,安装pip本身这个步骤就可以跳过了。

    如果你正在使用Windows,请参考安装Python一节的内容,确保安装时勾选了pipAdd python.exe to Path

    在命令提示符窗口下尝试运行pip,如果Windows提示未找到命令,可以重新运行安装程序添加pip

    注意:Mac或Linux上有可能并存Python 3.x和Python 2.x,因此对应的pip命令是pip3

    现在,让我们来安装一个第三方库——Python Imaging Library,这是Python下非常强大的处理图像的工具库。不过,PIL目前只支持到Python 2.7,并且有年头没有更新了,因此,基于PIL的Pillow项目开发非常活跃,并且支持最新的Python 3。

    一般来说,第三方库都会在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,等等。

    模块搜索路径

    当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错:

    >>> import mymodule
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ImportError: No module named mymodule
    

    默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

    >>> import sys
    >>> sys.path
    ['', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages']
    

    如果我们要添加自己的搜索目录,有两种方法:

    一是直接修改sys.path,添加要搜索的目录:

    >>> import sys
    >>> sys.path.append('/Users/michael/my_py_scripts')
    

    这种方法是在运行时修改,运行结束后失效。

    第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。

    模块

    内置模块是Python自带的功能,在使用内置模块相应的功能时,需要【先导入】再【使用】

    一、sys

    用于提供对Python解释器相关的操作:

    sys.argv           命令行参数List,第一个元素是程序本身路径
    sys.exit(n)        退出程序,正常退出时exit(0)
    sys.version        获取Python解释程序的版本信息
    sys.maxint         最大的Int值
    sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
    sys.platform       返回操作系统平台名称
    sys.stdin          输入相关
    sys.stdout         输出相关
    sys.stderror       错误相关
    

    进度

    import sys
    import time
    
    
    def view_bar(num, total):
        rate = float(num) / float(total)
        rate_num = int(rate * 100)
        r = '
    %d%%' % (rate_num, )
        sys.stdout.write(r)
        sys.stdout.flush()
    
    
    if __name__ == '__main__':
        for i in range(0, 100):
            time.sleep(0.1)
            view_bar(i, 100)
    
    进度百分比
    

    二、os

    用于提供系统级别的操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    os.getcwd()                 获取当前工作目录,即当前python脚本工作的目录路径
    os.chdir("dirname")         改变当前脚本工作目录;相当于shell下cd
    os.curdir                   返回当前目录: ('.')
    os.pardir                   获取当前目录的父目录字符串名:('..')
    os.makedirs('dir1/dir2')    可生成多层递归目录
    os.removedirs('dirname1')   若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
    os.mkdir('dirname')         生成单级目录;相当于shell中mkdir dirname
    os.rmdir('dirname')         删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
    os.listdir('dirname')       列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
    os.remove()                 删除一个文件
    os.rename("oldname","new")  重命名文件/目录
    os.stat('path/filename')    获取文件/目录信息
    os.sep                      操作系统特定的路径分隔符,win下为"\",Linux下为"/"
    os.linesep                  当前平台使用的行终止符,win下为" ",Linux下为" "
    os.pathsep                  用于分割文件路径的字符串
    os.name                     字符串指示当前使用平台。win->'nt'; Linux->'posix'
    os.system("bash command")   运行shell命令,直接显示
    os.environ                  获取系统环境变量
    os.path.abspath(path)       返回path规范化的绝对路径
    os.path.split(path)         将path分割成目录和文件名二元组返回
    os.path.dirname(path)       返回path的目录。其实就是os.path.split(path)的第一个元素
    os.path.basename(path)      返回path最后的文件名。如何path以/或结尾,那么就会返回空值。即os.path.split(path)的第二个元素
    os.path.exists(path)        如果path存在,返回True;如果path不存在,返回False
    os.path.isabs(path)         如果path是绝对路径,返回True
    os.path.isfile(path)        如果path是一个存在的文件,返回True。否则返回False
    os.path.isdir(path)         如果path是一个存在的目录,则返回True。否则返回False
    os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
    os.path.getatime(path)      返回path所指向的文件或者目录的最后存取时间
    os.path.getmtime(path)      返回path所指向的文件或者目录的最后修改时间

    三、hashlib

    用于加密相关的操作,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import hashlib
     
    # ######## md5 ########
    hash = hashlib.md5()
    # help(hash.update)
    hash.update(bytes('admin', encoding='utf-8'))
    print(hash.hexdigest())
    print(hash.digest())
     
     
    ######## sha1 ########
     
    hash = hashlib.sha1()
    hash.update(bytes('admin', encoding='utf-8'))
    print(hash.hexdigest())
     
    # ######## sha256 ########
     
    hash = hashlib.sha256()
    hash.update(bytes('admin', encoding='utf-8'))
    print(hash.hexdigest())
     
     
    # ######## sha384 ########
     
    hash = hashlib.sha384()
    hash.update(bytes('admin', encoding='utf-8'))
    print(hash.hexdigest())
     
    # ######## sha512 ########
     
    hash = hashlib.sha512()
    hash.update(bytes('admin', encoding='utf-8'))
    print(hash.hexdigest())

    以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。

    1
    2
    3
    4
    5
    6
    7
    import hashlib
     
    # ######## md5 ########
     
    hash = hashlib.md5(bytes('898oaFs09f',encoding="utf-8"))
    hash.update(bytes('admin',encoding="utf-8"))
    print(hash.hexdigest())

    python内置还有一个 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密

    1
    2
    3
    4
    5
    import hmac
     
    h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))
    h.update(bytes('admin',encoding="utf-8"))
    print(h.hexdigest())

    四、random

    import random
     
    print(random.random())
    print(random.randint(1, 2))
    print(random.randrange(1, 10))
    
       

    随机验证码

    import random
    checkcode = ''
    for i in range(4):
        current = random.randrange(0,4)
        if current != i:
            temp = chr(random.randint(65,90))
        else:
            temp = random.randint(0,9)
        checkcode += str(temp)
    print checkcode
    
    随机验证码
    

    五、re

    python中re模块提供了正则表达式相关操作

    常用正则表达式符号

    '.'     默认匹配除
    之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
    '^'     匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","
    abc
    eee",flags=re.MULTILINE)
    '$'     匹配字符结尾,或e.search("foo$","bfoo
    sdfsf",flags=re.MULTILINE).group()也可以
    '*'     匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac")  结果为['abb', 'ab', 'a']
    '+'     匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
    '?'     匹配前一个字符1次或0次
    '{m}'   匹配前一个字符m次
    '{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
    '|'     匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
    '(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c
     
     
    'A'    只从字符开头匹配,re.search("Aabc","alexabc") 是匹配不到的
    ''    匹配字符结尾,同$
    'd'    匹配数字0-9
    'D'    匹配非数字
    'w'    匹配[A-Za-z0-9]
    'W'    匹配非[A-Za-z0-9]
    's'     匹配空白字符、	、
    、
     , re.search("s+","ab	c1
    3").group() 结果 '	'
     
    '(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","381581199306143242").groupdict("city") 结果{'province': '3815', 'city': '81', 'birthday': '1993'}
    

     最常用的匹配语法

    re.match 从头开始匹配
    re.search 匹配包含
    re.findall 把所有匹配到的字符放到以列表中的元素返回
    re.splitall 以匹配到的字符当做列表分隔符
    re.sub      匹配字符并替换 
    

    字符:

      . 匹配除换行符以外的任意字符
      w 匹配字母或数字或下划线或汉字
      s 匹配任意的空白符
      d 匹配数字
       匹配单词的开始或结束
      ^ 匹配字符串的开始
      $ 匹配字符串的结束

    次数:

      * 重复零次或更多次
      + 重复一次或更多次
      ? 重复零次或一次
      {n} 重复n次
      {n,} 重复n次或更多次
      {n,m} 重复n到m次

    反斜杠的困扰
    与大多数编程语言相同,正则表达式里使用""作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\"表示。同样,匹配一个数字的"\d"可以写成r"d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。

    仅需轻轻知道的几个匹配模式

    re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
    M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)
    S(DOTALL): 点任意匹配模式,改变'.'的行为
    

    match

    # match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
     
     
     match(pattern, string, flags=0)
     # pattern: 正则模型
     # string : 要匹配的字符串
     # falgs  : 匹配模式
         X  VERBOSE     Ignore whitespace and comments for nicer looking RE's.
         I  IGNORECASE  Perform case-insensitive matching.
         M  MULTILINE   "^" matches the beginning of lines (after a newline)
                        as well as the string.
                        "$" matches the end of lines (before a newline) as well
                        as the end of the string.
         S  DOTALL      "." matches any character at all, including the newline.
     
         A  ASCII       For string patterns, make w, W, , B, d, D
                        match the corresponding ASCII character categories
                        (rather than the whole Unicode categories, which is the
                        default).
                        For bytes patterns, this flag is the only available
                        behaviour and needn't be specified.
          
         L  LOCALE      Make w, W, , B, dependent on the current locale.
         U  UNICODE     For compatibility only. Ignored for string patterns (it
                        is the default), and forbidden for bytes patterns.
    

    实例:

    # 无分组
            r = re.match("hw+", origin)
            print(r.group())     # 获取匹配到的所有结果
            print(r.groups())    # 获取模型中匹配到的分组结果
            print(r.groupdict()) # 获取模型中匹配到的分组结果
    
            # 有分组
    
            # 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
    
            r = re.match("h(w+).*(?P<name>d)$", origin)
            print(r.group())     # 获取匹配到的所有结果
            print(r.groups())    # 获取模型中匹配到的分组结果
            print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
    
    Demo
    

     search

    # search,浏览整个字符串去匹配第一个,未匹配成功返回None
    # search(pattern, string, flags=0)
    

     实例:

    # 无分组
    
            r = re.search("aw+", origin)
            print(r.group())     # 获取匹配到的所有结果
            print(r.groups())    # 获取模型中匹配到的分组结果
            print(r.groupdict()) # 获取模型中匹配到的分组结果
    
            # 有分组
    
            r = re.search("a(w+).*(?P<name>d)$", origin)
            print(r.group())     # 获取匹配到的所有结果
            print(r.groups())    # 获取模型中匹配到的分组结果
            print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
    
    demo
    

     findall

    # findall,获取非重复的匹配列表;如果有一个组则以列表形式返回,且每一个匹配均是字符串;如果模型中有多个组,则以列表形式返回,且每一个匹配均是元祖;
    # 空的匹配也会包含在结果中
    #findall(pattern, string, flags=0)
    

     实例:

    # 无分组
            r = re.findall("aw+",origin)
            print(r)
    
            # 有分组
            origin = "hello alex bcd abcd lge acd 19"
            r = re.findall("a((w*)c)(d)", origin)
            print(r)
    
    Demo
    

     sub

    # sub,替换匹配成功的指定位置字符串
     
    sub(pattern, repl, string, count=0, flags=0)
    # pattern: 正则模型
    # repl   : 要替换的字符串或可执行对象
    # string : 要匹配的字符串
    # count  : 指定匹配个数
    # flags  : 匹配模式
    

     实例:

     # 与分组无关
    
            origin = "hello alex bcd alex lge alex acd 19"
            r = re.sub("aw+", "999", origin, 2)
            print(r)
    

     split

    # split,根据正则匹配分割字符串
     
    split(pattern, string, maxsplit=0, flags=0)
    # pattern: 正则模型
    # string : 要匹配的字符串
    # maxsplit:指定分割个数
    # flags  : 匹配模式
    

     实例:

    # 无分组
            origin = "hello alex bcd alex lge alex acd 19"
            r = re.split("alex", origin, 1)
            print(r)
    
            # 有分组
            
            origin = "hello alex bcd alex lge alex acd 19"
            r1 = re.split("(alex)", origin, 1)
            print(r1)
            r2 = re.split("(al(ex))", origin, 1)
            print(r2)
    
    Demo
    

    序列化

    Python中用于序列化的两个模块

    • json     用于【字符串】和 【python基本数据类型】 间进行转换
    • pickle   用于【python特有的类型】 和 【python基本数据类型】间进行转换

    Json模块提供了四个功能:dumps、dump、loads、load

    pickle模块提供了四个功能:dumps、dump、loads、load

     

    七、configparser

    configparser用于处理特定格式的文件,其本质上是利用open来操作文件。

    指定格式

    # 注释1
    ;  注释2
     
    [section1] # 节点
    k1 = v1    # 值
    k2:v2       # 值
     
    [section2] # 节点
    k1 = v1    # 值
    
    指定格式
    

    1、获取所有节点

    1
    2
    3
    4
    5
    6
    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
    ret = config.sections()
    print(ret)

    2、获取指定节点下所有的键值对

    1
    2
    3
    4
    5
    6
    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
    ret = config.items('section1')
    print(ret)

    3、获取指定节点下所有的建

    1
    2
    3
    4
    5
    6
    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
    ret = config.options('section1')
    print(ret)

    4、获取指定节点下指定key的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
     
     
    v = config.get('section1', 'k1')
    # v = config.getint('section1', 'k1')
    # v = config.getfloat('section1', 'k1')
    # v = config.getboolean('section1', 'k1')
     
    print(v)

    5、检查、删除、添加节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
     
     
    # 检查
    has_sec = config.has_section('section1')
    print(has_sec)
     
    # 添加节点
    config.add_section("SEC_1")
    config.write(open('xxxooo', 'w'))
     
    # 删除节点
    config.remove_section("SEC_1")
    config.write(open('xxxooo', 'w'))

    6、检查、删除、设置指定组内的键值对

    import configparser
     
    config = configparser.ConfigParser()
    config.read('xxxooo', encoding='utf-8')
     
    # 检查
    has_opt = config.has_option('section1', 'k1')
    print(has_opt)
     
    # 删除
    config.remove_option('section1', 'k1')
    config.write(open('xxxooo', 'w'))
     
    # 设置
    config.set('section1', 'k10', "123")
    config.write(open('xxxooo', 'w'))
    
       

    time和datatime模块

    时间相关的操作,时间有三种表示方式:

    • 时间戳               1970年1月1日之后的秒,即:time.time()
    • 格式化的字符串    2014-11-11 11:11,    即:time.strftime('%Y-%m-%d')
    • 结构化时间          元组包含了:年、日、星期等... time.struct_time    即:time.localtime()
    #_*_coding:utf-8_*_
    
    import time
    
    
    # print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改成了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来
    # print(time.altzone)  #返回与utc时间的时间差,以秒计算
    # print(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016",
    # print(time.localtime()) #返回本地时间 的struct time对象格式
    # print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式
    
    # print(time.asctime(time.localtime())) #返回时间格式"Fri Aug 19 11:14:16 2016",
    #print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上
    
    
    
    # 日期字符串 转成  时间戳
    # string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式
    # print(string_2_struct)
    # #
    # struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳
    # print(struct_2_stamp)
    
    
    
    #将时间戳转为字符串格式
    # print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式
    # print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将utc struct_time格式转成指定的字符串格式
    
    
    
    
    
    #时间加减
    import datetime
    
    # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
    #print(datetime.date.fromtimestamp(time.time()) )  # 时间戳直接转成日期格式 2016-08-19
    # print(datetime.datetime.now() )
    # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天
    # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天
    # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时
    # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分
    
    
    #
    # c_time  = datetime.datetime.now()
    # print(c_time.replace(minute=3,hour=2)) #时间替换
    
     
    DirectiveMeaningNotes
    %a Locale’s abbreviated weekday name.  
    %A Locale’s full weekday name.  
    %b Locale’s abbreviated month name.  
    %B Locale’s full month name.  
    %c Locale’s appropriate date and time representation.  
    %d Day of the month as a decimal number [01,31].  
    %H Hour (24-hour clock) as a decimal number [00,23].  
    %I Hour (12-hour clock) as a decimal number [01,12].  
    %j Day of the year as a decimal number [001,366].  
    %m Month as a decimal number [01,12].  
    %M Minute as a decimal number [00,59].  
    %p Locale’s equivalent of either AM or PM. (1)
    %S Second as a decimal number [00,61]. (2)
    %U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3)
    %w Weekday as a decimal number [0(Sunday),6].  
    %W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3)
    %x Locale’s appropriate date representation.  
    %X Locale’s appropriate time representation.  
    %y Year without century as a decimal number [00,99].  
    %Y Year with century as a decimal number.  
    %z Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].  
    %Z Time zone name (no characters if no time zone exists).  
    %% A literal '%' character.

     

    paramiko

    paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。

    1、下载安装

    pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto
    pip3 install pycrypto
    pip3 install paramiko
    

     2、模块使用

    执行命令 - 名户名+密码

    #!/usr/bin/env python
    #coding:utf-8
    
    import paramiko
    
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect('192.168.1.108', 22, 'alex', '123')
    stdin, stdout, stderr = ssh.exec_command('df')
    print stdout.read()
    ssh.close();
    
    执行命令 - 用户名+密码
    

     执行命令 - 密钥

    import paramiko
    
    private_key_path = '/home/auto/.ssh/id_rsa'
    key = paramiko.RSAKey.from_private_key_file(private_key_path)
    
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect('主机名 ', 端口, '用户名', key)
    
    stdin, stdout, stderr = ssh.exec_command('df')
    print stdout.read()
    ssh.close()
    
    执行命令 - 密钥
    

     上传或下载文件 - 用户名+密码

    import os,sys
    import paramiko
    
    t = paramiko.Transport(('182.92.219.86',22))
    t.connect(username='wupeiqi',password='123')
    sftp = paramiko.SFTPClient.from_transport(t)
    sftp.put('/tmp/test.py','/tmp/test.py') 
    t.close()
    
    
    import os,sys
    import paramiko
    
    t = paramiko.Transport(('182.92.219.86',22))
    t.connect(username='wupeiqi',password='123')
    sftp = paramiko.SFTPClient.from_transport(t)
    sftp.get('/tmp/test.py','/tmp/test2.py')
    t.close()
    
    上传或下载文件 - 用户名+密码
    

     上传或下载文件 - 密钥

    import paramiko
    
    pravie_key_path = '/home/auto/.ssh/id_rsa'
    key = paramiko.RSAKey.from_private_key_file(pravie_key_path)
    
    t = paramiko.Transport(('182.92.219.86',22))
    t.connect(username='wupeiqi',pkey=key)
    
    sftp = paramiko.SFTPClient.from_transport(t)
    sftp.put('/tmp/test3.py','/tmp/test3.py') 
    
    t.close()
    
    import paramiko
    
    pravie_key_path = '/home/auto/.ssh/id_rsa'
    key = paramiko.RSAKey.from_private_key_file(pravie_key_path)
    
    t = paramiko.Transport(('182.92.219.86',22))
    t.connect(username='wupeiqi',pkey=key)
    
    sftp = paramiko.SFTPClient.from_transport(t)
    sftp.get('/tmp/test3.py','/tmp/test4.py') 
    
    t.close()
    
    上传或下载文件 - 密钥
    

    logging模块  

    logging模块提供了一个全功能和灵活的日志系统。最简单的,日志消息发送到文件或sys.stderr

    import logging
    logging.debug('Debugging information')
    logging.info('Informational message')
    logging.warning('Warning:config file %s not found', 'server.conf')
    logging.error('Error occurred')
    logging.critical('Critical error -- shutting down')
    

     这将生成以下输出:

    WARNING:root:Warning:config file server.conf not found
    ERROR:root:Error occurred
    CRITICAL:root:Critical error -- shutting down
    

    默认情况下,信息和调试消息被压制并输出到标准错误。其他输出选项包括将消息通过email、 datagrams、sockets发送,或者发送到 HTTP 服务器。新过滤器可以根据消息优先级选择不同的路由:DEBUGINFOWARNINGERRORCRITICAL

    日志系统可以直接在 Python 代码中定制,也可以不经过应用程序直接在一个用户可编辑的配置文件中加载。

    很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()info()warning()error() and critical() 5个级别,下面我们看一下怎么用。

    最简单用法

    import logging
     
    logging.warning("user [alex] attempted wrong password more than 3 times")
    logging.critical("server is down")
     
    #输出
    WARNING:root:user [alex] attempted wrong password more than 3 times
    CRITICAL:root:server is down
    

    看一下这几个日志级别分别代表什么意思

    LevelWhen it’s used
    DEBUG Detailed information, typically of interest only when diagnosing problems.
    INFO Confirmation that things are working as expected.
    WARNING An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
    ERROR Due to a more serious problem, the software has not been able to perform some function.
    CRITICAL A serious error, indicating that the program itself may be unable to continue running.

      

    如果想把日志写到文件里,也很简单

    import logging
     
    logging.basicConfig(filename='example.log',level=logging.INFO)
    logging.debug('This message should go to the log file')
    logging.info('So should this')
    logging.warning('And this, too')
    

     其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。

    logging.basicConfig(filename='example.log',level=logging.INFO)
    

     感觉上面的日志格式忘记加上时间啦,日志不知道时间怎么行呢,下面就来加上!

    import logging
    logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
    logging.warning('is when this event was logged.')
     
    #输出
    12/12/2010 11:46:36 AM is when this event was logged.
    

    日志格式

    %(name)s

    Logger的名字

    %(levelno)s

    数字形式的日志级别

    %(levelname)s

    文本形式的日志级别

    %(pathname)s

    调用日志输出函数的模块的完整路径名,可能没有

    %(filename)s

    调用日志输出函数的模块的文件名

    %(module)s

    调用日志输出函数的模块名

    %(funcName)s

    调用日志输出函数的函数名

    %(lineno)d

    调用日志输出函数的语句所在的代码行

    %(created)f

    当前时间,用UNIX标准的表示时间的浮 点数表示

    %(relativeCreated)d

    输出日志信息时的,自Logger创建以 来的毫秒数

    %(asctime)s

    字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒

    %(thread)d

    线程ID。可能没有

    %(threadName)s

    线程名。可能没有

    %(process)d

    进程ID。可能没有

    %(message)s

    用户输出的消息

      

    如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了


    Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

    logger提供了应用程序可以直接使用的接口;

    handler将(logger创建的)日志记录发送到合适的目的输出;

    filter提供了细度设备来决定输出哪条日志记录;

    formatter决定日志记录的最终输出格式。

    logger
    每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
    LOG=logging.getLogger(”chat.gui”)
    而核心模块可以这样:
    LOG=logging.getLogger(”chat.kernel”)

    Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
    Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
    Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
    Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别

    handler

    handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
    Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
    Handler.setFormatter():给这个handler选择一个格式
    Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象


    每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
    1) logging.StreamHandler
    使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
    StreamHandler([strm])
    其中strm参数是一个文件对象。默认是sys.stderr


    2) logging.FileHandler
    和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
    FileHandler(filename[,mode])
    filename是文件名,必须指定一个文件名。
    mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。

    3) logging.handlers.RotatingFileHandler
    这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
    RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
    其中filename和mode两个参数和FileHandler一样。
    maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
    backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。


    4) logging.handlers.TimedRotatingFileHandler
    这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
    TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
    其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
    interval是时间间隔。
    when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
    S 秒
    M 分
    H 小时
    D 天
    W 每星期(interval==0时代表星期一)
    midnight 每天凌晨

    import logging
     
    #create logger
    logger = logging.getLogger('TEST-LOG')
    logger.setLevel(logging.DEBUG)
     
     
    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
     
    # create file handler and set level to warning
    fh = logging.FileHandler("access.log")
    fh.setLevel(logging.WARNING)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     
    # add formatter to ch and fh
    ch.setFormatter(formatter)
    fh.setFormatter(formatter)
     
    # add ch and fh to logger
    logger.addHandler(ch)
    logger.addHandler(fh)
     
    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    

     日志等级:

    CRITICAL = 50
    FATAL = CRITICAL
    ERROR = 40
    WARNING = 30
    WARN = WARNING
    INFO = 20
    DEBUG = 10
    NOTSET = 0
    

     注:只有【当前写等级】大于【日志等级】时,日志文件才被记录。

    多文件日志

    对于上述记录日志的功能,只能将日志记录在单文件中,如果想要设置多个日志文件,logging.basicConfig将无法完成,需要自定义文件和日志操作对象。

    日志1

    # 定义文件
    file_1_1 = logging.FileHandler('l1_1.log', 'a', encoding='utf-8')
    fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s")
    file_1_1.setFormatter(fmt)
    
    file_1_2 = logging.FileHandler('l1_2.log', 'a', encoding='utf-8')
    fmt = logging.Formatter()
    file_1_2.setFormatter(fmt)
    
    # 定义日志
    logger1 = logging.Logger('s1', level=logging.ERROR)
    logger1.addHandler(file_1_1)
    logger1.addHandler(file_1_2)
    
    
    # 写日志
    logger1.critical('1111')
    
    日志一
    

     日志二

    # 定义文件
    file_2_1 = logging.FileHandler('l2_1.log', 'a')
    fmt = logging.Formatter()
    file_2_1.setFormatter(fmt)
    
    # 定义日志
    logger2 = logging.Logger('s2', level=logging.INFO)
    logger2.addHandler(file_2_1)
    
    日志(二)
    
  • 相关阅读:
    基于SOA分布式架构的dubbo框架基础学习篇
    项目管理 绩效考核
    性能测试晋级教程
    从页面走向单元实现真正的业务驱动
    微软的开源Sonar工具测试网站的性能和安全性
    2.动手实操Apache ZooKeeper
    1. Apache ZooKeeper快速课程入门
    开发人员的福音:微软、谷歌、Mozilla将他们所有的Web API文档放在同一个地方
    Happy Java:定义泛型参数的方法
    比较两个文件不同以及生成SQL插入语句
  • 原文地址:https://www.cnblogs.com/zhangyux/p/6037736.html
Copyright © 2011-2022 走看看