模块与包

递归

递归调用:在调用一个函数的过程中,直接或间接地调用了函数本身
直接
def func():
    print('from func')
    func()

func()

间接
def foo():
    print('from foo')
    bar()

def bar():
    print('from bar')
    foo()

foo()
递归的执行分为两个阶段:
1 递推
2 回溯

递归特性:

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

堆栈扫盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html 

尾递归优化:http://egon09.blog.51cto.com/9161406/1842475

二分查找

data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
def binary_search(dataset,find_num):
    print(dataset)
 
    if len(dataset) >1:
        mid = int(len(dataset)/2)
        if dataset[mid] == find_num:  #find it
            print("找到数字",dataset[mid])
        elif dataset[mid] > find_num :# 找的数在mid左面
            print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
            return binary_search(dataset[0:mid], find_num)
        else:# 找的数在mid右面
            print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
            return binary_search(dataset[mid+1:],find_num)
    else:
        if dataset[0] == find_num:  #find it
            print("找到数字啦",dataset[0])
        else:
            print("没的分了,要找的数字[%s]不在列表里" % find_num)
 
 
binary_search(data,66)

 协程函数

协程函数就是使用了yield表达式形式的生成器

def eater(name):
    print("%s eat food" %name)
    while True:
        food = yield
    print("done")

g = eater("gangdan")
print(g)

结果:
<generator object eater at 0x0000026AFE4968E0>
这里就证明了g现在就是生成器函数

1. 2 协程函数赋值过程

用的是yield的表达式形式

要先运行next(),让函数初始化并停在yield,相当于初始化函数,然后再send() ,send会给yield传一个值
** next()和send() 都是让函数在上次暂停的位置继续运行,

next是让函数初始化

send在触发下一次代码的执行时,会给yield赋值

def eater(name):
    print('%s start to eat food' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s get %s ,to start eat' %(name,food))
        food_list.append(food)


e=eater('钢蛋')  # wrapper('')
# print(e)
print(next(e))     # 现在是运行函数,让函数初始化
print(e.send('包子'))  #
print(e.send('韭菜馅包子'))
print(e.send('大蒜包子'))

这里的关键是:
要先运行next()函数

用装饰器函数把next()函数先运行一次:

import os
def init(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)
        return res
    return wrapper
@init
def search(target):
    while True:
        search_path=yield
        g=os.walk(search_path)
        for par_dir,_,files in files:
            for file in files:
                file_abs_path=r'%s\%s' %(par_dir,file)
                #print(file_abs_path)
                target.send(file_abs_path)

1.2 协程函数的应用

过滤一个文件下的子文件、字文件夹的内容中的相应的内容,在Linux中的命令就是 grep -rl 'error /dir

#grep -rl 'error' /dir/
import os
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

#第一阶段:找到所有文件的绝对路径
@init
def search(target):
    while True:
        filepath=yield
        g=os.walk(filepath)
        for pardir,_,files in g:
            for file in files:
                abspath=r'%s\%s' %(pardir,file)
                target.send(abspath)
# search(r'C:\Users\Administrator\PycharmProjects\python18期周末班\day5\aaa')
# g=search()
# g.send(r'C:\Python27')

#第二阶段:打开文件
@init
def opener(target):
    while True:
        abspath=yield
        with open(abspath,'rb') as f:
            target.send((abspath,f))




#第三阶段:循环读出每一行内容
@init
def cat(target):
    while True:
        abspath,f=yield #(abspath,f)
        for line in f:
            res=target.send((abspath,line))
            if res:break



#第四阶段:过滤
@init
def grep(pattern,target):
    tag=False
    while True:
        abspath,line=yield tag
        tag=False
        if pattern in line:
            target.send(abspath)
            tag=True


#第五阶段:打印该行属于的文件名
@init
def printer():
    while True:
        abspath=yield
        print(abspath)

g = search(opener(cat(grep('os'.encode('utf-8'), printer()))))
# g.send(r'C:\Users\Administrator\PycharmProjects\python18期周末班\day5\aaa')

g.send(r'C:\Users\Administrator\PycharmProjects\python18期周末班')
#a1.txt,a2.txt,b1.txt

面向过程的编程思想:流水线式的编程思想,在设计程序时,需要把整个流程设计出来

优点:
1:体系结构更加清晰
2:简化程序的复杂度

缺点:
1:可扩展性极其的差,所以说面向过程的应用场景是:不需要经常变化的软件,如:linux内核,httpd,git等软件

模块

1,什么是模块(python里叫模块,其它叫类库)?

一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀

  1 使用python编写的代码(.py文件)

  2 已被编译为共享库或DLL的C或C++扩展

  3 包好一组模块的包

  4 使用C编写并链接到python解释器的内置模块

类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。

2,为何要使用模块?

如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理,这时我们不仅仅可以把这些文件当作脚本去执行,还可以把他们当作模块来导入到其他的模块中,实现了功能的重复利用。

 示例文件:spam.py,文件名spam.py,模块名spam

 1 #spam.py
 2 print('from the spam.py')
 3 
 4 money=1000
 5 
 6 def read1():
 7     print('spam->read1->money',money)
 8 
 9 def read2():
10     print('spam->read2 calling read')
11     read1()
12 
13 def change():
14     global money
15     money=0

模块分三种:

自定义模块

内置模块

第三方模块

     安装:pip3

             源码

自定义模块

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

import

 模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句)如下:

 import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
 import spam
 import spam
 import spam 
 '''
 执行结果:
 from the spam.py

导入模块

Python之所以应用越来越广泛,在一定程度上也依赖于其为程序员提供了大量的模块以供使用,如果想要使用模块,则需要导入(导入模块其实就是告诉Python解释器去解释那个py文件)导入模块有一下几种方法:

1,import module
2,from module.xx.xx import xx
3,from module.xx.xx import xx as rename (为模块名起别名,相当于rename=r)
3,1 as示范用法
   if file_format == 'xml':    import xmlreader as reader    elif file_format == 'csv':    import csvreader as reader    data=reader.read_date(filename)
4,from module.xx.xx import *

在一行导入多个模块

 import sys,os,re

我们可以从sys.module中找到当前已经加载的模块,sys.module是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

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

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

那么问题来了,导入模块时是根据那个路径作为基准来进行的呢?即:sys.path

import sys
print(sys.path)

结果:
['C:\\Users\\Administrator\\PycharmProjects\\python18\\day5', 'C:\\Users\\Administrator\\PycharmProjects\\python18', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\python36.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\DLLs', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\lib', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages']

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

import sys
import os
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path)

导入模块干的事:

1,产生新的名称空间

2,以新建的名称空间为全局名称空间,执行文件的代码

3,拿到一个模块名spam,指向spam.py产生的名称空间

 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

money与spam.money不冲突
 2 #test.py
 3 import spam 
 4 money=10
 5 print(spam.money)
 6 
 7 '''
 8 执行结果:
 9 from the spam.py
10 1000
11 '''

from......import......

对比import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式

而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了、

需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法

from spam import read1,read2

这样在当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=1000
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

如果当前有重名read1或者read2,那么会有覆盖效果。

 1 from spam import money,read1
 2 money=100 #将当前位置的名字money绑定到了100
 3 print(money) #打印当前的名字
 4 read1() #读取spam.py中的名字money,仍然为1000
 5 
 6 '''
 7 from the spam.py
 8 100
 9 spam->read1->money 1000
10 '''

也支持as

 from spam import read1 as read

也支持导入多行

1 from spam import (read1,
2                   read2,
3                   money)

from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

可以使用__all__来控制*(用来发布新版本)

在spam.py中新增一行

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

如果spam.py中的名字前加_,即_money,则from spam import *,则_money不能被导入

 把模块当做脚本执行 

我们可以通过模块的全局变量__name__来查看模块名:
当做脚本运行:
__name__ 等于'__main__'(系统的内置变量)

当做模块导入:
__name__=

作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
if __name__ == '__main__':

模块搜索路径

python解释器在启动时会自动加载一些模块,可以使用sys.modules查看

在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用

如果没有,解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。

所以总结模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

搜索路径:
当一个命名为spam的模块被导入时
    解释器首先会从内建模块中寻找该名字
    找不到,则去sys.path中找该名字

sys.path从以下位置初始化
    1 执行文件所在的当前目录
    2 PTYHONPATH(包含一系列目录名,与shell变量PATH语法一样)
    3 依赖安装时默认指定的

注意:在支持软连接的文件系统中,执行脚本所在的目录是在软连接之后被计算的,换句话说,包含软连接的目录不会被添加到模块的搜索路径中

在初始化后,我们也可以在python程序中修改sys.path,执行文件所在的路径默认是sys.path的第一个目录,在所有标准库路径的前面。这意味着,当前目录是优先于标准库目录的,需要强调的是:我们自定义的模块名不要跟python标准库的模块名重复,除非你是故意的,傻叉。

 编译python模块

为了提高加载模块的速度,强调强调强调:提高的是加载速度而绝非运行速度。python解释器会在__pycache__目录中下缓存每个模块编译后的版本,格式为:module.version.pyc。通常会包含python的版本号。例如,在CPython3.3版本下,spam.py模块会被缓存成__pycache__/spam.cpython-33.pyc。这种命名规范保证了编译后的结果多版本共存。

 

Python检查源文件的修改时间与编译的版本进行对比,如果过期就需要重新编译。这是完全自动的过程。并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc使一种跨平台的字节码,类似于JAVA火.NET,是由python虚拟机来执行的,但是pyc的内容跟python的版本相关,不同的版本编译后的pyc文件不同,2.5编译的pyc文件不能到3.5上执行,并且pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的。

 

python解释器在以下两种情况下不检测缓存
  1 如果是在命令行中被直接导入模块,则按照这种方式,每次导入都会重新编译,并且不会存储编译后的结果(python3.3以前的版本应该是这样)

  2 如果源文件不存在,那么缓存的结果也不会被使用,如果想在没有源文件的情况下来使用编译后的结果,则编译后的结果必须在源目录下

提示:

1.模块名区分大小写,foo.py与FOO.py代表的是两个模块

2.你可以使用-O或者-OO转换python命令来减少编译模块的大小

1 -O转换会帮你去掉assert语句
2 -OO转换会帮你去掉assert语句和__doc__文档字符串
3 由于一些程序可能依赖于assert语句或文档字符串,你应该在在确认需要的情况下使用这些选项。

3.在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的

4.只有使用import语句是才将文件自动编译为.pyc文件,在命令行或标准输入中指定运行脚本则不会生成这类文件,因而我们可以使用compieall模块为一个目录中的所有模块创建.pyc文件

1 模块可以作为一个脚本(使用python -m compileall)编译Python源
2  
3 python -m compileall /module_directory 递归着编译
4 如果使用python -O -m compileall /module_directory -l则只一层
5  
6 命令行里使用compile()函数时,自动使用python -O -m compileall
7  
8 详见:https://docs.python.org/3/library/compileall.html#module-compileall

dir函数

内建函数dir是用来查找模块中定义的名字,返回一个有序字符串列表
import spam
dir(spam)

如果没有参数,dir()列举出当前定义的名字


dir()不会列举出内建函数或者变量的名字,它们都被定义到了标准模块builtin中,可以列举出它们,
import builtins
dir(builtins)

一,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       错误相关

二、os   用于提供系统级别的操作(提供对操作系统进行调用的借口)

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下为"\t\n",Linux下为"\n"
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所指向的文件或者目录的最后修改时间
二 包(包是一种通过使用模块名来组织python模块名称空间的方式)
1,无论是import形式还是from.......import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法

2,包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)

3,import导入文件时,产生名称空间中的名字来源于文件,import包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

强调:

  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

  2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块

注意事项
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。

2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

3.对比import item 和from item import name的应用场景:
如果我们想直接使用name那必须使用后者。

绝对导入和相对导入

我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:

绝对导入:以glance作为起始

相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

1 在glance/api/version.py
2 
3 #绝对导入
4 from glance.cmd import manage
5 manage.main()
6 
7 #相对导入
8 from ..cmd import manage
9 manage.main()

测试结果:注意一定要在于glance同级的文件中测试

from glance.api import versions 

注意:在使用pycharm时,有的情况会为你多做一些事情,这是软件相关的东西,会影响你对模块导入的理解,因而在测试时,一定要回到命令行去执行,模拟我们生产环境,你总不能拿着pycharm去上线代码吧!!!

特别需要注意的是:可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。

软件开发规范 

logging日志(日志处理之logging模块)

日志级别:

debug     最详细的调试信息  10

info         就想记录一下    20

warning   出现警告     30

error        出现错误    40

crttcal       非常严重    50

从大至小,级别越高,数字越大!

日志格式

%(name)s

Logger的名字

%(levelno)s

数字形式的日志级别

%(levelname)s

文本形式的日志级别

%(pathname)s

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

%(filename)s

谁调用日志输出该函数的模块的文件名(aa.py)

%(module)s

谁调用日志输出该函数的模块名(aa)

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

用户输出的消息

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
logging.basicConfig(filename='example.log',
       level=logging.WARNING,
       format='%(asctime)s %(message)s',
       datefmt='%m/%d/%Y %H:%M:%S %p')
#filename='example.log' 指定写入日志文件
#level=logging.WARNING(日志级别必须大写),日志级别达到WARNING级别写入db文件(文件默认生成)
#format='%(asctime)s %(message)s'日志格式, asctime当前时间, message当前内容
#datefmt='%m/%d/%Y %H:%M:%S %p'  日期格式logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.error("wrong password more than 3 times")
logging.critical("server is down")
#db文件显示
31/07/2017 14:01:54 AM And this, too06/07/2017 01:01:54 AM wrong password more than 3 times

31/07/2017 14:01:54 AM server is down 

日志模式输入到全局日志,db日志文件,终端

import logging#日志输入到全局日志,db日志文件 终端
logger = logging.getLogger('TEST-LOG')      #TEST-LOG是日志名
logger.setLevel(logging.DEBUG)              #logging.DEBUG 全局的日志级别
# 全局日志基本最高,子logging基本必须比全局高 否则不显示日志输出
ch = logging.StreamHandler()                      #创建屏幕输出显示
ch.setLevel(logging.INFO)                         #屏幕输出日志级别
fh = logging.FileHandler("access.log")         #文件的hander
fh.setLevel(logging.WARNING)                   #文件的日志级别

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)          #定义日志格式
fh.setFormatter(formatter)          #定义日志格式
logger.addHandler(ch)               #屏幕输出绑定到logger接口
logger.addHandler(fh)               #文件输出绑定到logger接口#应用程序代码
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

日志输入到全局日志,db日志文件 屏幕 并按照时间切割保留最近3个db文件,每5秒切割1次

import logging
from logging import handlers   #创建db文件日志切割,不加切割不用写
#日志输入到全局日志,db日志文件 屏幕 并安装时间日志切割保留最近3个文件,每5秒切割1次
logger = logging.getLogger('TEST-LOG')      #TEST-LOG是日志名
logger.setLevel(logging.DEBUG)              #logging.DEBUG 全局的日志级别

# 全局日志基本最高,子logging基本必须比全局高 否则不显示日志输出 ch = logging.StreamHandler() #创建屏幕输出显示 ch.setLevel(logging.INFO) #屏幕输出日志级别 #fh = logging.FileHandler("access.log") #文件显示(db文件保存日志) 不加日志切割用这个 fh = handlers.TimedRotatingFileHandler("access.log",when="S",interval=5,backupCount=3) #文件显示(db文件保存日志) #TimedRotatingFileHandler 按时间切割保存, when="S",单位是秒(h是小时), interval=5,(5个单位切1次)backupCount=3 (保存最近3次,0是无限制保存) #fh = handlers.RotatingFileHandler("access.log",maxBytes=4,backupCount=3) #RotatingFileHandler 按时间切割保存 maxBytes=4 (文件满4bytes,就生成新文件) backupCount=3 (保存最近3次,0是无限制保存) fh.setLevel(logging.WARNING) #文件的日志级别 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) #定义日志格式 fh.setFormatter(formatter) #定义日志格式 logger.addHandler(ch) #屏幕输出绑定到logger接口 logger.addHandler(fh) #文件输出绑定到logger接口 # 应用程序代码 logger.debug('debug message') logger.info('info message') logger.warning('warn message') logger.error('error message') logger.critical('critical message')

 

posted on 2017-06-02 21:24  龚旭1994  阅读(129)  评论(0编辑  收藏  举报