【Python之路Day5】基础篇

今日目录

多层装饰器

迭代器和生成器

递归

字符串格式化

模块 

序列化相关模块

time、datetime模块

logging模块

 

一. 多层装饰器

还是上一篇的那个例子,关于用户管理程序:登录用户管理程序,查看用户信息的时候,系统要提示登录,登录验证成功后普通用户可以查看自己信息,管理员登录后才可以进入管理界面,普通用户提示权限不足,这样一来,就可以重新写下程序,来两个装饰器来装饰。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)


#先定义一个用户字典,判断用户状态,用户身份等
USER_INFO = {'user_state': False, 'admin_flag':2}

#搞一个装饰器,装饰管理后台,检查用户身份等
def outer(func):
    def inner(*args,**kwargs):
        if USER_INFO['user_state']:
            result = func(*args,**kwargs)
            return result
        else:
            print('你没有登陆系统,请登陆后再操作!')
    return inner

#在定义一个check_manager装饰器,用户检测用户登录身份是管理员还是普通用户
def check_manager(func):
    def inner(*args, **kwargs):
        if USER_INFO['admin_flag'] == 0:
            result = func(*args,**kwargs)
            return result
        else:
            print('权限不足!')
    return inner

#管理后台函数被装饰的时候,从下到上渲染,解释执行的时候是从上到下执行,所以先使用outer装饰, 再使用check_manager
@outer
@check_manager
def manager():
    print('欢迎登陆到管理员界面')

#检查用户身份函数
@outer
def checkuser(username):
    if USER_INFO['admin_flag'] == 0:
        print('{} 您的用户是管理员身份'.format(username))
    else:
        print('{} 普通用户啦'.format(username))

#登录函数
def login(username,userpwd):
    if username == 'tom' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 0
        manager()
    elif username == 'jerry' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 1
    else:
        print('用户名或者密码错误!')
        #print('欢迎登陆 %s' %username)

#主函数
def main():
    while True:
        print('1: 管理后台   2: 登陆   3. 检查用户身份  4. 退出')
        user_input_num = input('选择下吧:').strip()
        if user_input_num == '1':
            result = manager()
        elif user_input_num == '2':
            username = input('请输入您的用户名:').strip()
            userpwd = input('密码').strip()
            login(username,userpwd)
        elif user_input_num == '3':
            checkuser(USER_INFO.get('current_user',None))
        elif user_input_num == '4':
            print('Bye')
            break

if __name__ == '__main__':
    main()

使用两个装饰器装饰一个函数
使用两个装饰器装饰一个函数

使用一个多层装饰器来装饰同一个用户身份、权限等;

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)


#先定义一个用户字典,判断用户状态,用户身份等
USER_INFO = {'user_state': False, 'admin_flag':2}

#搞一个装饰器,装饰管理后台,检查用户身份等
def outer(func):
    def inner(*args,**kwargs):
        if USER_INFO['user_state']:
            if USER_INFO['admin_flag'] == 0:
                result = func(*args,**kwargs)
                return result
            else:
                print('权限不足!')
        else:
            print('你没有登陆系统,请登陆后再操作!')
    return inner

# 在定义一个check_manager装饰器,用户检测用户登录身份是管理员还是普通用户
# def check_manager(func):
#     def inner(*args, **kwargs):
#         if USER_INFO['admin_flag'] == 0:
#             result = func(*args,**kwargs)
#             return result
#         else:
#             print('权限不足!')
#     return inner

#只使用一个装饰器装饰
@outer
def manager():
    print('欢迎登陆到管理员界面')

#检查用户身份函数
@outer
def checkuser(username):
    if USER_INFO['admin_flag'] == 0:
        print('{} 您的用户是管理员身份'.format(username))
    else:
        print('{} 普通用户啦'.format(username))

#登录函数
def login(username,userpwd):
    if username == 'tom' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 0
        manager()
    elif username == 'jerry' and userpwd == '123':
        USER_INFO['user_state'] = True
        USER_INFO['current_user'] = username
        USER_INFO['admin_flag'] = 1
    else:
        print('用户名或者密码错误!')
        #print('欢迎登陆 %s' %username)

#主函数
def main():
    while True:
        print('1: 管理后台   2: 登陆   3. 检查用户身份  4. 退出')
        user_input_num = input('选择下吧:').strip()
        if user_input_num == '1':
            result = manager()
        elif user_input_num == '2':
            username = input('请输入您的用户名:').strip()
            userpwd = input('密码').strip()
            login(username,userpwd)
        elif user_input_num == '3':
            checkuser(USER_INFO.get('current_user',None))
        elif user_input_num == '4':
            print('Bye')
            break

if __name__ == '__main__':
    main()

使用多层装饰器
使用多层装饰器

注意,使用多层装饰器,渲染的顺序是从下往上,而解释是从上往下。正如上面例子中‘使用两个装饰器装饰一个函数’一样,先使用@outer装饰,再使用@check_manager来装饰。解释的时候是先解释outer,而后再是check_manager。因此执行的时候如果没有登录,会先提示登录,满足后,验证权限。

 

二. 迭代器和生成器

1. 迭代器

迭代器,首先来说是也是一个对象。它知道如何从一个对象中一次取出一个元素,并且跟踪它当前所在序列的位置。还记得之前的文章中反复提到的for循环遍历一个可迭代的对象么?比如说列表:

l1 = [11,22,33,44,55,]
for i in l1:
    print(i)

#执行上面代码返回:
11
22
33
44
55

再比如说,字符串:

name = 'daniel'
for i in name:
    print(i)

#执行返回:
d
a
n
i
e
l

或者元组,字典:

#元组
t1 = (11,22,33,44,55)
for i in t1:
    print(i)

#返回
11
22
33
44
55

#字典
d1 = {'name':'daniel','Age':18}
for i in d1:
    print(i)
#返回:
name
Age

其实在后台,for循环对对象执行了iter()函数,iter()是Python的内置函数,定义了__next__()方法的迭代器对象,在容器中逐个访问对象中的元素,取完最后一个值之后,会抛出异常(StopIteration), 通知for循环结束。比如下面的方法,定义一个字符串s1 = 'daniel':

>>> s1 = 'daniel'
#字符串本身没有__next__方法
>>> s1.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__next__'

#调用iter() 封装s1,并赋值给name
>>> name = iter(s1)

#查看类型,此时变成一个迭代器(str_iterator)
>>> type(name)
<class 'str_iterator'>
#使用__next__()方法逐个调用
>>> name.__next__()
'd'
>>> name.__next__()
'a'
>>> name.__next__()
'n'
>>> name.__next__()
'i'
>>> name.__next__()
'e'
>>> name.__next__()
'l'
#全部取完元素之后,会抛异常!(StopIteration)
>>> name.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> l1 = [11,22,33,44,55,]
>>> l1.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'
>>> result = iter(l1)
>>> type(result)
<class 'list_iterator'>


>>> result.__next__()
11
>>> result.__next__()
22
>>> result.__next__()
33
>>> result.__next__()
44
>>> result.__next__()
55
>>> result.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
列表其他也都一样

所以,iter()只会被调用一次,而__next__()会被调用N次!

2. 生成器

生成器是使用函数创造,如果函数里出现了 'yield' 关键字,这个函数就成为一个生成器。如下代码:

#函数f1,里面使用了yield关键字
def f1():
    print('f1')
    yield 11
    yield 22
    yield 33

#查看类型,就已经是一个生成器了!
print(type(f1()))

#输出:
<class 'generator'>

而后可以使用__next__()方法调用,每次进入函数找到yield关键字,取出yield后面的数据。并且记录这次的位置,下一次还从当前位置开始。

print(result.__next__())
print('-'*20)
print(result.__next__())
print('-'*20)
print(result.__next__())
print('-'*20)

#打印结果如下:
f1
11
--------------------
22
--------------------
33
--------------------

#如果还继续的话,也会抛异常()
print(result.__next__())
print('-'*20)

StopIteration
def f1():
    print('f1')
    yield 11
    yield 22
    yield 33

result = f1()
for i in result:
    print(i)
也可以使用再使用for遍历

或者自己定义一个myrange的函数

def myrange(args):
    start_value = 0
    while 1:
        if start_value > args:
            return
        else:
            yield start_value
            start_value += 1

#接受用户输入的一个终止值
user_input=input('一个终止值:').strip()
#赋值,并传给myrange
result = myrange(int(user_input))

#循环遍历打印result
for i in result:
    print(i)

3. 迭代器和生成器的区别

每个生成器都是一个迭代器,但是反过来就不行。生成器能做到迭代器所做的所有的事儿,而且生成器显得特别简洁,而且也是高效的。

一个带yield关键字的函数就是一个生成器,它和普通函数不同,生成一个 generator 看起来像是函数调用,但是不会执行任何函数代码,直到对其调用 __next__()才开始执行(或者for循环)。

两者在取完对象,在调用取用时都会抛出 StopIteration 的异常。

生成器是通过调用一个或者多个yield表达式构成的并且满足迭代器的定义。

两者的相同点: 对象迭代完成后就不能再重写迭代了

 

三. 递归

在函数内部,可以调用其他函数:

def a():
    return 'a'

def b():
    r = a()
    return r

def c():
    r = b()
    return r

def d():
    r = c()
    return r

result = d()
print(result)

如果一个函数在内部调用自己本身,那么这个函数就是一个递归函数。如:

def testf(func):
    func += 1
    if func > 4:
        print(func)
        return
    return testf(func)

testf(1)

#执行结果,返回
5
递归函数

思考题:

  使用递归函数方式计算: 1 * 2*3*4*5*6*7的值。

def sikao(func):
    if func == 7:   #如果传func等于7,return func
        return func
    return func * sikao(func+1)   #调用函数自身,用func乘以func+1值

result = sikao(1)  #将1作为实际参数传给函数
print(result)        #打印值
思考题

 

四. 字符串格式化

 1. %方式

%方式是相对较老的字符串格式化方式,语法格式是:

%[(name)][flags][width].[precision]typecode

解释如下:

name:(可选) 命名,用于字典型的控制赋值

>>> s1 = 'My name is %(name)s' %{'name':'daniel'}
>>> print(s1)
My name is daniel

flags(可选):需要配合width使用,值可以是:

+   右对齐, 正数前面加正号,负数前面加上负号

-    左对齐,正数前面无符号,负数前面加负号

0    右对齐,正数前面加上空格,负数前面加上负号,数字时用0填充;

空格  右对齐, 正数前面加空格,负数前面加负号

>>> s1 = 'My name is %+20s' %'daniel'
>>> print(s1)
My name is               daniel
>>> s1 = 'My name is %+20s age: %-10d' %('daniel',18)
>>> print(s1)
My name is               daniel age: 18 

#空格,右对齐,正数前面加空格,负数前面加负号
>>> s1 = 'My name is % 20s age: %- 20d, money: %- 10d' %('daniel',18,-10)
>>> print(s1)
My name is               daniel age:  18                 , money: -10

#0, 右对齐方式,正数前面无负号,负数前面加负号,在数字前面用0填充
>>> s1 = 'My name is %020s age: %020d, money: %010d' %('daniel',18,-10)
>>> print(s1)
My name is               daniel age: 00000000000000000018, money: -000000010

width (可选项)   宽度,总长度

>>> print('%10s'%'name')
      name
>>> print('%10d'%20)
        20
>>> print('%-10d'%20)
20        
>>> print('%+10d'%20)
       +20
>>> print('%010d'%20)
0000000020
>>> print('% 10d'%20)
        20

precision(可选项):  精度,如果数字是浮点数的话,可以设定显示数字的显示精度,如四舍五入到2位小数点:

>>> print('%.2f'%3.1315926)
3.13

typecode(必选项), 这块主要是选择数据类型的,常用的如下:

%s  字符串,获取对象str()方法的返回值

>>> print('%s' %'name')
name

%r  字符串,获取对象repr()方法的返回值

>>> name = '%r'%'name'
>>> type(name)
<class 'str'>
>>> name
"'name'"
%r

%c  单个字符, 将数字转换为unicode对应的值(还记得上个文章说的ASCII表么?),10进制范围为0 <=i <= 1114111

>>> print('%c' %a)
A
>>> a = 66
>>> print('%c' %a)
B
>>> a = 89
>>> print('%c' %a)
Y
%c

 

%d  十进制整数

>>> a = 20
>>> print('%d'%a)
20
>>> print('%d'%a)
20
>>> a = 25
>>> print('%d'%a)
25
%a

%i   十进制整数

>>> print('%i'%a)
25
>>> a = 89
>>> print('%i'%a)
89
>>> a = 8901
>>> print('%i'%a)
8901
%i

%o  八进制整数, 将整数转换为8进制表示

>>> a = 8901
>>> print('%o'%a)
21305
>>> a = 10
>>> print('%o'%a)
12
%o

%x  十六进制整数, 将整数转换为16进制表示

>>> a = 10
>>> print('%x'%a)
a
>>> a = 15
>>> print('%x'%a)
f
>>> a = 12330
>>> print('%x'%a)
302a
%x

%e  指数(小e)

>>> a = 12330
>>> print('%e'%a)
1.233000e+04
%e

%E  指数(大写E)

>>> a = 12330
>>> print('%E'%a)
1.233000E+04
%E

%f   浮点数,默认显示到小数点后6位

>>> a = 3.1415926
>>> print('%f'%a)
3.141593
%f

%F  浮点数,默认显示到小数点后6位

>>> a = 3.1415926
>>> print('%F'%a)
3.141593
%F

%g  指数e或浮点数(根据长度显示)

>>> a = 1300882
>>> print('%g'%a)
1.30088e+06
%g

%G  指数E或浮点数(根据长度显示)

>>> a = 1300882
>>> print('%G'%a)
1.30088E+06
%G

注意,要想在%方式格式化字符串中输出%,必须要使用%%来转译!

>>> print('%s balabala %%'%'name')
name balabala %

如果在格式化输出的时候加#,在o、i、d、x前面会添加对应的进制表示,如:

>>> a = 123
>>> print('%#o' %a)
0o173
>>> print('%#x' %a)
0x7b
#十进制输出不变
>>> print('%#d' %a)
123
>>> print('%#i' %a)
123
View Code

 

2. format方式

format格式是比较新的字符串格式化方式,大有替代%s的方式,不过目前官方没有明确表示要替换掉%s. 相比%s方式,format更加强大;

格式:

[[fill]align][sign][#][0][width][,][.precision][type]
  • fill(可选项)   空白处填充的字符,需要配合width使用
  • >>> print('{:#^30} balabala'.format('tianchong'))
    ##########tianchong########### balabala
    View Code
  • align(可选项)   对其方式,也需要配合width使用

<    左对齐

>    右对齐

=    右对齐, 将符号放在填充字符的左侧,而且只是对数字类型生效,如果使用字符串会报错!

^    居中显示

#居中显示,并以#号填充
>>> print('{:#^30} balabala'.format('tianchong'))
##########tianchong########### balabala
#左对齐
>>> print('{:#<30}'.format('tianchong'))
tianchong#####################
#右对齐
>>> print('{:#>30}'.format('tianchong'))
#####################tianchong
#=使用字符串会报错
>>> print('{:#=30}'.format('tianchong'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: '=' alignment not allowed in string format specifier
#使用数字ok
>>> print('{:#=30}'.format(600))
###########################600
View Code
  • sign(可选项)   有无符号数字

+  正加正、负加负

-   正不变、负加负

空格  正好空格, 负加负

#+, 正加正,负加负
>>> print('{:#^+30}'.format(600))
#############+600#############
#-, 正不变,负加负
>>> print('{:#^-30}'.format(600))
#############600##############
#空格, 正加空格,负加负
>>> print('{:#^ 30}'.format(600))
############# 600#############
View Code
  • #(可选项)       对二进制、八进制、十六进制,加上后会显示 0b 0o 0x,默认不显示
  • >>> print('{:o}'.format(600))
    1130
    #加上#号,会在二进制、八进制、十六进制前面打印对应的进制标识
    >>> print('{:#o}'.format(600))
    0o1130
    >>> print('{:#x}'.format(600))
    0x258
    >>> print('{:#b}'.format(600))
    0b1001011000
    >>> print('{:#d}'.format(600))
    600
    View Code
  • , (可选项)       为数字添加分隔符, 如财务计算金钱,或者银行账户显示 1,322,220,220
  • >>> print('{:,d}'.format(203301010293))
    203,301,010,293
    View Code
  • width(可选项)      宽度
  • #一般都是配合fill、align和sign一起使用
    >>> print('{:10,d}'.format(203301010293))
    203,301,010,293
    View Code
  • .precision(可选项)   小数点精度
  • #默认显示小数点位数后面6位
    >>> print('{:f}'.format(3.1415926))
    3.141593
    >>> print('{:F}'.format(3.1415926))
    3.141593
    
    #可以指定精度
    >>> print('{:.2F}'.format(3.1415926))
    3.14
    >>> print('{:.2f}'.format(3.1415926))
    3.14
    View Code
  • type (可选项)     格式化类型 

  字符串类型的参数:

字符型:

  s    字符串类型

空白   未指定类型,默认是None,同于s

整数型:

b   10进制转换为2进制并实现格式化

c    10进制转换为unicode字符,还是上个文章对应的那个ASCII表

d    10进制整数

o    将10进制转换为8进制

x    10进制转换为16进制,小x表示

X    10进制转换为16进制,大X表示

浮点型参数:

e  科学技术法,用小e表示

E  科学技术法,大E

>>> print('{:e}'.format(1415926))
1.415926e+06
>>> print('{:E}'.format(1415926))
1.415926E+06
>>> print('{:g}'.format(1415926))
1.41593e+06
>>> print('{:G}'.format(1415926))
1.41593E+06
e/E/g/G

f   浮点数,默认保留小数点后6位

F   浮点数,默认保留小数点后6位

g   自动在e和f中切换,如果是e表示为小e

G  自动在E和F中切换,如果是E表示为大E

% 显示百分比,默认也是小数点后6位

>>> print('{:%}'.format(0.7926))
79.260000%
>>> print('{:.2%}'.format(0.7926))
79.26%
%

 

五. 模块

简单的说,模块就是包含了一组Python代码的文件,模块里可以是类、函数、变量(如配置文件),也可以是一组可执行代码。

Python中的模块有内置模块、第三方模块和自定义模块;

1. 内置模块

安装完Python之后,Python自带了一部分常用的模块(库),供开发者使用,这就是内置模块,也叫作标准模块,或者标准库。比如: sys,os,time,datetime,json等等...

我的Mac上的安装路径在:

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5

里面全部.py的文件(小的)或者文件夹(大的),查看路径方式如下:

>>> import sys
>>> result = sys.path

>>> for i in result:
...   print(i)
... 

/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
View Code

使用这些模块是,先import导入,而后再使用即可!

2. 第三方模块

Python 社区有着牛逼到爆的第三方模块,提供给开发者使用,加速开发速度,直接用现成的轮子即可,不需要重复造轮子。据说可以满足完成所有能想到的事情(别钻牛角尖哈),甚至你想不到的也是ok的。地址 https://pypi.python.org/pypi

安装第三方模块:

安装第三方模块的方式简单的不行,主要有两种,一种是通过setuptools工具完成;另一种是下载源码然后安装。

先来看下setuptools

Python中有两种工具都封装了setuptools包管理工具,一个是easy_install; 一个是pip(python2叫pip,python3叫pip3,并且python3自带pip3),推荐使用pip。(当然python3得用pip3哈,不要钻牛角尖哈)

  • 使用pip3安装一个模块wrfy:
root@test3-ubunut:~# pip3 install wrfy
Downloading/unpacking wrfy
  Downloading wrfy-0.1.0.tar.gz
  Running setup.py (path:/tmp/pip_build_root/wrfy/setup.py) egg_info for package wrfy
    
Downloading/unpacking docker-py==1.8.1 (from wrfy)
  Downloading docker_py-1.8.1-py2.py3-none-any.whl (41kB): 41kB downloaded
Downloading/unpacking progressbar2==3.5.0 (from wrfy)
  Downloading progressbar2-3.5.0.tar.gz
  Running setup.py (path:/tmp/pip_build_root/progressbar2/setup.py) egg_info for package progressbar2
    
    warning: no files found matching 'AUTHORS.rst'
    warning: no files found matching 'README.txt'
Downloading/unpacking requests>=2.5.2 (from docker-py==1.8.1->wrfy)
  Downloading requests-2.10.0-py2.py3-none-any.whl (506kB): 506kB downloaded
Downloading/unpacking websocket-client>=0.32.0 (from docker-py==1.8.1->wrfy)
  Downloading websocket_client-0.37.0.tar.gz (194kB): 194kB downloaded
  Running setup.py (path:/tmp/pip_build_root/websocket-client/setup.py) egg_info for package websocket-client
    
Downloading/unpacking backports.ssl-match-hostname>=3.5 (from docker-py==1.8.1->wrfy)
  Downloading backports.ssl_match_hostname-3.5.0.1.tar.gz
  Running setup.py (path:/tmp/pip_build_root/backports.ssl-match-hostname/setup.py) egg_info for package backports.ssl-match-hostname
    
Requirement already satisfied (use --upgrade to upgrade): six>=1.4.0 in /usr/lib/python3/dist-packages (from docker-py==1.8.1->wrfy)
Installing collected packages: wrfy, docker-py, progressbar2, requests, websocket-client, backports.ssl-match-hostname
  Running setup.py install for wrfy
    
    Installing wrfy script to /usr/local/bin
  Running setup.py install for progressbar2
    
    warning: no files found matching 'AUTHORS.rst'
    warning: no files found matching 'README.txt'
  Found existing installation: requests 2.2.1
    Not uninstalling requests at /usr/lib/python3/dist-packages, owned by OS
  Running setup.py install for websocket-client
    changing mode of build/scripts-3.4/wsdump.py from 644 to 755
    
    changing mode of /usr/local/bin/wsdump.py to 755
  Running setup.py install for backports.ssl-match-hostname
    
Successfully installed wrfy docker-py progressbar2 requests websocket-client backports.ssl-match-hostname
Cleaning up...
pip3 install wrfy
  • 源码安装:

先下载源码到目录,而后解压:

root@test2-ubunut:~# tar xf mycloud-0.51.tar.gz 

root@test2-ubunut:~# cd mycloud-0.51/

安装:

python3 setup.py install 
  • 使用安装的模块:
#先导入模块import
>>> import wrfy
#而后就可以使用了
>>> dir(wrfy)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
View Code

3. 自定义模块:

还记得上周作业么,学习了函数,也开始尝试使用函数式作业完成需求,写完整个作业之后,发现有500多行代码。要想做个编辑啥的,维护起来就费劲了。

好吧,这个500多行如果说还好的话,那么如果程序码了50000行呢?50W,500W呢?显而易见,一个文件是不行的,就应该按照模块的观念,将相同功能的模块放在一个文件中,各个文件之间通过导入来调用。如Python自带的time库就是提供了所有关于time的方法等等。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

'''
Author: DBQ
Blog: http://www.cnblogs.com/dubq/articles/5543466.html
Github: https://github.com/daniel-vv/ops
'''

import time
import re
import os


USER_STATUS = {'user_status':False,'username':False,'user_type':False} #全局用户状态字典变量, user_status: 登录成功后会置为True
                                                                       # username:  登录成功后会将值置为用户名,便于在后面引用
                                                                       # user_type: 默认普通用户为False,管理员登录置为True
LOCK_FILE = 'locked.db'  #黑名单数据库文件
USERDB = 'passwd'        #用户数据库文件
USERDB_NEW = 'passwd.new' #用户临时文件
LOCK_FILE_NEW = 'locked.db.new'

def validate_email(email):
    '''
    检测用户输入邮箱地址合法性
    :param email: 接受用户传入的形式参数
    :return: 地址合法返回True,否则为False
    '''
    if len(email)>7:
        if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",email) != None:
            return True
    return False

def valid_phone(phone):
    '''
    合法性检测,主要检测用户输入电话号码是否合法
    :param phone: 接受形式参数
    :return: True合法,False不合法
    '''
    if len(phone) == 11:
        if re.match("[1]{1}[3,5,7,8,9]{1}[0-9]{9}",phone) != None:
            return True
    return False

def valid_str(string):
    '''
    合法性检测,主要检测用户用户名是否合法,必须是字母开头,并且大于两位的字符,不包含特殊字符
    :param phone: 接受形式参数
    :return: True合法,False不合法
    '''
    if len(string) != 0:
        if re.match("^[a-zA-Z][a-zA-Z0-9]{1,10}$",string) != None:
            return True
    return False

def check_user(func):
    '''
    检查用户是否登录装饰器,主要装饰修改密码,查询用户,删除用户,提示权限四个模块
    :param func:
    :return:
    '''
    def inner(*args,**kwgras):
        if  USER_STATUS['username']:
            result = func(*args,**kwgras)
            return result
        else:
            if input('\033[31;1m您还没有登录,瞎逛什么呢,登录再说!!按下任意键继续...\033[0m'):pass
    return inner


def login():
    '''
    用户登录函数,输入密码错误三次,并且是同一个用户的话,将锁定这个用户
    :return: True,表示认证成功, 还会返回用户名username
    '''
    Count = 0
    user_init = ''
    user_lock_count = 1
    flag = False
    while Count<3:
        username = input('请输入您的用户名:').strip()
        if username:
            password = input('请输入您的密码: ').strip()
            with open(LOCK_FILE,'r') as lockfile:
                for i in lockfile:
                    if username == i.strip():
                        print('\033[31;1m抱歉, [ {} ]用户已经被锁定, 请联系管理员解锁!\033[0m'.format(username))
                        return False

            if username == user_init:
                user_lock_count += 1
                # print(user_lock_count,username,user_init)
            else:
                user_init = username
                user_lock_count -= 1
                # print(user_lock_count,username,user_init)
            with open(USERDB,'r') as f:
                for i in f:
                    user,passwd,user_type = i.strip().split(':')[0],i.strip().split(':')[1],i.strip().split(':')[5]
                    if username == user and password == passwd:
                        print('\033[32;1m[ {} ] 欢迎登录用户管理系统\033[0m'.format(username))
                        flag = True
                        break
                else:
                    print('用户名或密码错误!')

            if flag:  #如何认证成功,更改默认全局变量里的用户属性信息,并返回True
                USER_STATUS['user_status'] = True
                USER_STATUS['username'] = username
                USER_STATUS['user_type'] = True if user_type == '0' else False  #为True表示用户是管理员身份,写到字典里待进一步判断
                return True
            if not flag:
                Count += 1
                continue
        else:
            continue
    else:
        # print(user_lock_count)
        # print(username,user_init)
        if user_lock_count == 2:  #如果用户计数器累加到2,锁定用户到黑名单文件
            with open(LOCK_FILE,'a+') as f:
                print('\033[31;1m十分抱歉的告诉您,输入次数超限,[ {} ]用户已经被锁定,请联系管理员解锁!'.format(username))
                f.write(username+'\n')
                time.sleep(1)


def registry_user():
    '''
    注册函数
    :return: 注册成功返回True和记录值, 失败返回False和记录值
    '''
    flag = False if not USER_STATUS['user_status'] else True
    Count = 0
    if flag and not USER_STATUS['user_type']: #如果是普通用户登录的状态下选择注册用户,
        user_input = input('\033[31;1m{} 您已经登录了,但是普通用户无法添加用户,你可以尝试退出账户,而后注册一个用户, 确定退出登录么? y/Y\033[0m'.format(USER_STATUS['username'])).strip()
        if user_input == 'y' or user_input == 'Y':
            flag = False
            USER_STATUS['user_status'] = False  #在字典里将标志位置为否表示退出用户登录
            USER_STATUS['username'] = False
            if input('\033[31;1m您已退出登录,按下任意键继续...\033[0m'):pass
        else:
            return False,None
    if flag and USER_STATUS['user_type']:  #如果用户已经登录,并且是管理员权限的话,直接添加用户
        flag = False
    while not flag:
        username = input('输入一个个性的名字吧:\033[31;1m(必填项)\033[0m  ').strip()
        if not valid_str(username):
            print('您输入的用户名不合法, 需要字母或者字母+数字组合,不能包含特殊字符,不能是数字开头,不能少于3个字符,谢谢.')
            continue
        else:
            Count += 1
        password = input('来一个牛逼一点的密码,前提是你能记得住哈:\033[31;1m(必填项)\033[0m  ').strip()
        password_verify = input('来吧,重复一遍你牛逼的密码:\033[31;1m(必填项)\033[0m  ').strip()
        if password != password_verify:
            print('\033[31;1m你输入的两次牛逼的密码不一致!\033[0m')
            continue
        else:
            Count += 1

        if not password or not password_verify:
            continue
        else:
            Count += 1
        homedir = input('输入你的用户家目录, 默认/home/username').strip()
        if not homedir:homedir = '/home/{}'.format(username)
        mailaddr = input('输入你的邮箱地址:\033[31;1m(必填项,格式:username@domain.com)\033[0m ').strip()
        if not validate_email(mailaddr):
            print('\033[31;1m邮箱输入不合法,我做了合法性检测,不要糊弄我!!!\033[0m')
            continue
        else:
            Count += 1
        user_shell = input('输入你的shell, 默认/bin/bash').strip()
        user_shell = user_shell if user_shell else '/bin/bash'
        user_status = 1
        user_phone = input('请输入电话号码,方便通知你有妹子找你.\033[31;1m(必填项)\033[0m ').strip()
        if not valid_phone(user_phone):  #调用电话号码合法性检测
            print('\033[31;1m输入的电话号码不合法,我做了合法性检测,不要糊弄我!!!\033[0m')
            continue
        else:
            Count += 1

        if Count >= 5:
            break

    record = '{}:{}:{}:{}:{}:{}:{}'.format(username,password,homedir,mailaddr,user_shell,user_status,user_phone)
    with open(USERDB,'r') as f:
        for i in f:
            if i.strip().split(':')[0] == username:
                if input('\033[31;1m用户名{}太受欢迎,已经被注册啦,再换一个吧,老兄! 按下任意键继续...\033[0m'.format(username)):pass
                return False,record
    return True,record


@check_user
def get_user(username):
    '''
    查询用户信息函数
    :param username: 接受用户参数
    :return: 暂时没用到
    '''

    if USER_STATUS['user_type']:
        user_input = input('\033[31;1m哇哇哇,管理员呐,请输入你的查询, 仅限于邮箱模糊查询,如(gmail.com)\033[0m   ').strip()
        with open('passwd','r') as f:
            flag = False
            for i in f:
                if user_input in i:
                    # print('True')
                    username,homedir = i.strip().split(':')[0],i.strip().split(':')[2]
                    emailaddr,user_shell,user_phone = i.strip().split(':')[3],i.strip().split(':')[4],i.strip().split(':')[6]
                    user_type = '管理员' if i.strip().split(':')[5] == '0' else '普通用户'
                    print('您模糊搜索到的包含{}的用户:'.center(50,'+').format(user_input))
                    print('''
                        用户名:      {}
                        家目录:      {}
                        邮箱地址:    {}
                        用户Shell:   {}
                        用户类型:    {}
                        联系电话:    {}
                    '''.format(username,homedir,emailaddr,user_shell,user_type,user_phone))
                    print('+'*63)
                    if input('\033[32;1m按下任意键继续...\033[0m'):pass
                    flag = True
            else:
                if not flag:
                    print('\033[31;1m没有匹配到包含 %s 的用户信息\033[0m'%user_input)


    else:
        # print(USER_STATUS['user_type'])
        print('\033[32;1m普通用户只能查看自己的信息啦,想看其他人,得提升权限,找管理员哈!\033[0m')
        with open(USERDB,'r') as f:
            for i in f:
                # print(i.strip().split(':')[0])
                user = i.strip().split(':')[0]
                homedir = i.strip().split(':')[2]
                email = i.strip().split(':')[3]
                userbash = i.strip().split(':')[4]
                phone = i.strip().split(':')[6]
                user_status = '管理员' if i.strip().split(':')[5] == '0' else '普通用户'
                if i.strip().split(':')[0] == username:
                   print('''
                      \033[32;1m[{}] 用户信息\033[0m
                   用户名:        {}
                   家目录:        {}
                   邮箱地址:      {}
                   用户shell:     {}
                   用户类型:      {}
                   联系电话:      {}
                   '''.format(user,user,homedir,email,userbash,user_status,phone))
                   if input('\033[32;1m按下任意键继续...\033[0m'):pass


@check_user
def edit_password(username):
    '''
    修改密码函数
    :param username: 接受用户传入形式参数用户名
    :return: True: 更改密码成功   False: 更改失败!
    '''
    flag = False
    Count = 0
    if not USER_STATUS['user_type']:  #如果用户是普通用户身份,只能修改自己密码
        with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
            for i in readonly_file: #遍历循环,并把每个值赋给变量,准备后面验证后拼接;
                # print(i.strip().split(':')[0])
                user,password = i.strip().split(':')[0],i.strip().split(':')[1],
                userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3],
                user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                if username != user:
                    write_file.write(i.strip()+'\n')
                else:
                    password_new = input('请输入您的新密码: ').strip()
                    password_new_verify = input('请再次输入您的新密码: ').strip()
                    if password_new == password_new_verify:  #做一次密码校验,验证两次输入是否一致
                        write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                        if input('\033[32;1m密码更改成功!任意键继续...\033[0m'):pass
                        flag = True
                    else:
                        if input('\033[31;1m两次输入的密码不一致!任意键继续...\033[0m'):pass
                        return False
                Count += 1
        if flag:
        # os.rename(USERDB_NEW,USERDB)   #如果更改成功,直接把老文件改名为源文件,实现改密码,并返回True
            with open(USERDB_NEW,'r') as f_new, open(USERDB,'w+') as f_old:
                for i in f_new.readlines():
                    if not i.split():
                        continue
                    else:
                        f_old.write(i)
            return True

    else: #如果用户身份是管理员的话,将可以更改用户自身密码,还有其他普通用户的密码;
        user_input = input('\033[31;1m哇哇哇,管理员呐,您要改谁的密码? 1:当前用户  2.其他用户\033[0m   ').strip()
        if user_input == '1':
            with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
                for i in readonly_file: #遍历循环,并把每个值赋给变量,准备后面验证后拼接;
                    user,password = i.strip().split(':')[0],i.strip().split(':')[1]
                    userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3],
                    user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                    if username != user:
                        write_file.write(i.strip()+'\n')
                    else:
                        password_new = input('请输入您的新密码: ').strip()
                        password_new_verify = input('请再次输入您的新密码: ').strip()
                        if password_new == password_new_verify:  #做一次密码校验,验证两次输入是否一致
                            write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                            if input('\033[32;1m密码更改成功!任意键继续...\033[0m'):pass
                            flag = True
                        else:
                            if input('\033[31;1m两次输入的密码不一致!任意键继续...\033[0m'):pass
                            return False

        if user_input == '2':
            user_input_name = input('\033[31;1m您要改谁的密码? 输入一个要更改密码的用户名: \033[0m   ').strip()
            # print(user_input_name)
            user_list = []
            username = user_input_name  #将用户输入的用户名赋值给username变量
            with open(USERDB,'r') as f:
                for user in f:
                    user_db = user.strip().split(':')[0]
                    user_list.append(user_db)
            if username in user_list:
                with open(USERDB,'r') as readonly_file ,open(USERDB_NEW,'w+') as write_file:
                    for i in readonly_file: #遍历循环,并把每个值赋给变量,准备后面验证后拼接;
                        user = i.strip().split(':')[0]
                        userdir,usermail = i.strip().split(':')[2],i.strip().split(':')[3]
                        user_shell,user_type,user_phone = i.strip().split(':')[4],i.strip().split(':')[5],i.strip().split(':')[6]
                        if username != user:
                            write_file.write(i.strip()+'\n')
                        else:
                            password_new = input('请输入您的新密码: ').strip()
                            password_new_verify = input('请再次输入您的新密码: ').strip()
                            if password_new == password_new_verify:  #做一次密码校验,验证两次输入是否一致
                                write_file.write('{}:{}:{}:{}:{}:{}:{}\n'.format(user,password_new,userdir,usermail,user_shell,user_type,user_phone))
                                if input('\033[32;1m密码更改成功!任意键继续...\033[0m'):pass
                                flag = True
                            else:
                                if input('\033[31;1m两次输入的密码不一致!任意键继续...\033[0m'%username):pass
                                return False
            else:
                print('\033[31m你输入的用户名[ %s ]不存在!\033[0m')
                return False

    if flag:
        # os.rename(USERDB_NEW,USERDB)   #如果更改成功,直接把老文件改名为源文件,实现改密码,并返回True
        a = 0
        with open(USERDB_NEW,'r') as f_new, open(USERDB,'w+') as f_old:
            for i in f_new:
                if a == 0:
                    f_old.write(i.strip())
                else:
                    f_old.write('\n'+i.strip())
                a += 1
        return True


@check_user
def update_user():
    '''
    提升用户权限函数,主要用户将普通管理员提升为管理员权限
    :return:
    '''
    username_list = [] #定义一个空列表,用户把所有的用户名抓到里面来,来判断提升用户权限的用户名是否存在
    flag = False
    if USER_STATUS['user_type']:
        username = input('\033[32;1m管理员,你要提升谁的权限为管理员? 来吧, 告诉我他/她的名字: \033[0m').strip()
        with open(USERDB,'r') as f:  #遍历文件,把所有用户名追加到列表中
            for i in f:
                user = i.strip().split(':')[0]
                username_list.append(user)
        if username not in username_list:  #如果用户名不存在的话,提示用户用户名不存在
            print('\033[31;1m滚粗,用户名根本不存在,逗谁呢!\033[0m')
            return False
        else:
            with open(USERDB,'r') as readfile,open(USERDB_NEW,'w+') as writefile:
                for line in readfile:
                    user_name,password,homedir = line.strip().split(':')[0],line.strip().split(':')[1],line.strip().split(':')[2]
                    emailaddr,user_shell,user_phone = line.strip().split(':')[3],line.strip().split(':')[4],line.strip().split(':')[6]
                    if username != user_name:
                        writefile.write(line.strip()+'\n')
                    else:
                        user_type = '0' if username == user_name else '1'
                        record = '{}:{}:{}:{}:{}:{}:{}\n'.format(user_name,password,homedir,emailaddr,user_shell,user_type,user_phone)
                        writefile.write(record)
                        print('\033[31;1m%s用户已经被你提升为管理员,哈!\033[0m'%username)
                        flag = True
    if flag:
        os.rename(USERDB_NEW,USERDB)
        time.sleep(1)
        return True


@check_user
def unlock_user():
    '''
    解锁用户函数,主要是管理员使用
    :return: 成功返回True,否则为False
    '''
    user_list = []
    flag = False
    if USER_STATUS['user_type']:  #如果用户是管理员可以执行这个
        user_input_name = input('请输入要解锁的用户名:').strip()
        if user_input_name:
            with open(LOCK_FILE,'r') as f,open(LOCK_FILE_NEW,'w+') as f1:
                for i in f:
                    if user_input_name == i.strip():
                        flag = True
                        continue
                    else:
                        f1.write(i)

        else:
            return False
    else:
        if input('\033[32;1m只有管理员可以解锁用户,普通用户不可进行此操作!\033[0m'):pass
        return False
    if flag:
        print('解锁成功!')
        os.rename(LOCK_FILE_NEW,LOCK_FILE)
        return True
    if not flag:
        print('\033[31;1m用户名[ %s ]不在黑名单列表中!\033[0m'%user_input_name)
        return False

@check_user
def del_user():
    '''
    删除用户函数
    :return: 用户删除成功返回True, 失败为False
    '''
    flag = False
    username_list = [] #定义一个空列表,用户把所有的用户名抓到里面来,来判断提用户是否存在
    if not USER_STATUS['user_type']:
        if input('\033[31;1m[ %s ],你不是管理员啦,谁也无法删除!\033[0m'%USER_STATUS['username']):pass
    else:
        user_input_name = input('\033[31;1m管理员[ %s ]先生/女士, 你要删谁? 给我个名字:  \033[0m'%USER_STATUS['username']).strip()
        if user_input_name:
            with open(USERDB,'r') as f_list:  #遍历文件,把所有用户名追加到列表中
                for i in f_list:
                    user = i.strip().split(':')[0]
                    username_list.append(user)
            if user_input_name in username_list:
                with open(USERDB,'r') as f,open(USERDB_NEW,'w+') as f1:
                    for line in f:
                        user_name,password,homedir = line.strip().split(':')[0],line.strip().split(':')[1],line.strip().split(':')[2]
                        emailaddr,user_shell,user_phone = line.strip().split(':')[3],line.strip().split(':')[4],line.strip().split(':')[6]
                        if user_input_name == user_name:
                            continue
                        else:
                            f1.write(line)
                            flag = True
            else:
                print('\033[31;1m抱歉, 你输入的用户名[ %s ]不存在!\033[0m '%user_input_name)
                return False
    if flag:
        os.rename(USERDB_NEW,USERDB)
        time.sleep(1)
        if input('\033[32;1m[ %s ]已经被删除!\033[0m '%user_input_name):pass
        return True


def print_fun():
    if USER_STATUS['username']:
        print('\033[35;1m欢迎 [%s] 来到用户管理系统\033[0m'.center(90,'#')%USER_STATUS['username'])
        print('''\033[35;1m
            1. 登录系统
            2. 添加用户
            3. 修改密码
            4. 查询用户
            5. 删除用户
            6. 提升权限
            7. 解锁用户
            8. 退出账户
        \033[0m''')
        print('#'*90)
    else:
        print('欢迎来到用户管理系统'.center(82,'#'))
        print('''
            1. 登录系统
            2. 注册用户
            3. 修改密码
            4. 查询用户
            5. 删除用户
            6. 提升权限
            7. 解锁用户
            8. 退出程序
        ''')
        print('#'*90)


def main():
    '''
    主函数,调用各个菜单函数
    :return: 暂时没用到
    '''
    while True:
        print_fun()
        user_input_num = input('请选择序列').strip()
        if user_input_num == '1':
            if not USER_STATUS['user_status']:  #先判断用户没有登录才调用实例
                result = login()
                if not result:   #如果result返回为False,表示认证失败,退出循环
                    break
            else: #为True的话,证明用户已经登录,不允许重复登录
                if input('\033[31;1m%s 你已经登录了,重复登录你想干什么???\033[0m'%USER_STATUS['username']):pass
        elif user_input_num == '2':  #如果输入2,用户进入改密码程序
            result,record = registry_user()  #实例化函数
            registry_flag = False #添加标志位
            if result:   #registry_user返回两个函数,如果result为真, 继续下面操作
                username = record.split(':')[0]  #切割下,把用户名切割成一个变量
                with open(USERDB,'a+') as f:     #打开文件以追加模式,把用户注册的信息写入到数据文件中
                    f.seek(0,2)
                    f.write('\n' + record)
                    if input('\033[31;1m[ %s ]注册成功,按下任意键继续...\033[0m'%username):pass
        elif user_input_num == '3':
            edit_password(USER_STATUS['username'])
        elif user_input_num == '4':
            get_user(USER_STATUS['username'])
        elif user_input_num == '5':
            del_user()
        elif user_input_num == '6':
            result = update_user()
        elif user_input_num == '7':
            unlock_user()
        elif user_input_num == '8':
            if USER_STATUS['user_status']:
                user_input_retry = input('\033[31;1m确定退出登录账户? y/Y').strip()
                if user_input_retry == 'y' or user_input_retry == 'Y':
                    print('[ %s ]你已经退出程序'%USER_STATUS['username'])
                    USER_STATUS['user_status'] = False
                    USER_STATUS['username'] = False
                    USER_STATUS['user_type'] = False
            else:
                print('bye!')
                break
        else:
            print('\033[31;1m不合法的序列!\033[0m')

if __name__ == '__main__':
    main()
上周作业代码

定义一个,如a.py文件里包含:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

def f1():
    print('这是a文件中的打印内容')

然后在b.py中导入使用,import, b.py中的内容:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

import a

a.f1()

执行b.py后结果如下:

这是a文件中的打印内容

4. 导入模块:

导入模块的几种方法:

#最常用的
import 模块名

#导入模块中的某个功能
from 模块名 import 功能
from 文件夹.模块 import 功能

#导入所有功能
from 模块 import *
#一般都不建议这么做

#还可以添加个别名
from 模块 import 功能 as 别名

一个模块只能被导入一次,不管你执行了多少次import,这样就是防止导入模块被一遍又一遍的执行。

实例,添加一个python解释器中的tab补全功能。新建一个tab.py文件,内容如下:

#!/usr/bin/env python 
# this content comes from oldboy trainning.
# e_mail:31333741@qq.com
# qqinfo:49000448
# function: python tab config.
# version:1.1 
################################################
# oldboy trainning info.      
# QQ 1986787350 70271111
# site:http://www.etiantian.org
# blog:http://oldboy.blog.51cto.com
# oldboy trainning QQ group: 208160987 45039636
################################################
# python startup file
import sys
import readline
import rlcompleter
import atexit
import os
# tab completion
readline.parse_and_bind('tab: complete')
# history file
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
try:
    readline.read_history_file(histfile)
except IOError:
    pass
atexit.register(readline.write_history_file, histfile)


del os, histfile, readline, rlcompleter
tab.py

将文件保存到Python的 sys.path中

>>> a = sys.path
>>> for i in a:
...   print(i)
... 

#这些路径是全部可以import到的路径
/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

#一般情况下,都将第三方的模块放到dist-packages目录下,这块,我们也把tab.py放到dist-packages目录下
import的路径

一般情况下,都将第三方的模块放到dist-packages目录下,这块,我们也把tab.py放到dist-packages目录下。

而后在python3解释器下import:

>>> import tab
>>> sys.
Display all 100 possibilities? (y or n)
sys.__class__(              sys._current_frames(        sys.getsizeof(
sys.__delattr__(            sys._debugmallocstats(      sys.getswitchinterval(
sys.__dict__                sys._getframe(              sys.gettrace(
sys.__dir__(                sys._home                   sys.hash_info
sys.__displayhook__(        sys._mercurial              sys.hexversion
sys.__doc__                 sys._xoptions               sys.implementation
sys.__eq__(                 sys.abiflags                sys.int_info
sys.__excepthook__(         sys.api_version             sys.intern(
sys.__format__(             sys.argv                    sys.maxsize
sys.__ge__(                 sys.base_exec_prefix        sys.maxunicode
sys.__getattribute__(       sys.base_prefix             sys.meta_path
sys.__gt__(                 sys.builtin_module_names    sys.modules
sys.__hash__(               sys.byteorder               sys.path
sys.__init__(               sys.call_tracing(           sys.path_hooks
sys.__interactivehook__(    sys.callstats(              sys.path_importer_cache
sys.__le__(                 sys.copyright               sys.platform
sys.__loader__(             sys.displayhook(            sys.prefix
sys.__lt__(                 sys.dont_write_bytecode     sys.ps1
sys.__name__                sys.exc_info(               sys.ps2
sys.__ne__(                 sys.excepthook(             sys.setcheckinterval(
sys.__new__(                sys.exec_prefix             sys.setdlopenflags(
sys.__package__             sys.executable              sys.setprofile(
sys.__reduce__(             sys.exit(                   sys.setrecursionlimit(
sys.__reduce_ex__(          sys.flags                   sys.setswitchinterval(
sys.__repr__(               sys.float_info              sys.settrace(
sys.__setattr__(            sys.float_repr_style        sys.stderr
sys.__sizeof__(             sys.getallocatedblocks(     sys.stdin
sys.__spec__                sys.getcheckinterval(       sys.stdout
sys.__stderr__              sys.getdefaultencoding(     sys.thread_info
sys.__stdin__               sys.getdlopenflags(         sys.version
sys.__stdout__              sys.getfilesystemencoding(  sys.version_info
sys.__str__(                sys.getprofile(             sys.warnoptions
sys.__subclasshook__(       sys.getrecursionlimit(      
sys._clear_type_cache(      sys.getrefcount(            
>>> sys.
需要补齐,按两下tab就行了

5. sys.path

上面说到sys.path的路径,如果不在这个环境变量,import是无法成功的

>>> a = sys.path
#类型是list列表
>>> type(a)
<class 'list'>

比如说如果在添加一个文件夹testfile, 下面添加一个文件test.py

root@test3-ubunut:~# mkdir testfile
root@test3-ubunut:~# touch testfile/test.py
root@test3-ubunut:~# vim testfile/test.py
#文件内容:

#!/usr/bin/env python3

def f1():
    print('这是来自testfile目录下test模块的输出')

而后在家目录下创建一个a.py文件,导入test.py,看能否成功:

root@test3-ubunut:~# pwd
/home/putao
root@test3-ubunut:~# vim a.py
#文件内容:

#!/usr/bin/env python3

from testfile import test

test.f1()

执行下试试。

root@test3-ubunut:~# python3 a.py 
这是来自testfile目录下test模块的输出

ok,这个能成功,原因是Python会去当前目录下找,因为testfile在当前家目录,所以能找到。好,现在,我把testfile文件夹放到/tmp目录下去试试:

# mv testfile /tmp/
#更改a.py

#!/usr/bin/env python3

from tmp.testfile import test
import sys

#print(sys.path)
test.f1()

#执行下试试:
root@test3-ubunut:~# python3 a.py 
Traceback (most recent call last):
  File "a.py", line 3, in <module>
    from tmp.testfile import test
ImportError: No module named 'tmp'

#找不到模块的
View Code

那么,我们这么试下,sys.path中我们添加一个环境变量值,/tmp

# 更改下a.py文件,先追加/tmp中到列表中,而后在导入testfile
#!/usr/bin/env python3

import sys
sys.path.append('/tmp')

from testfile import test
test.f1()

执行下看结果:

root@test3-ubunut:~# python3 a.py 
这是来自testfile目录下test模块的输出

六.  序列化相关模块

先理解下序列化和反序列化的感念:

序列化:把Python对象序列化成字符串

反序列化: 把字符串转换成Python对象

1.json

json模块的常用功能:

dumps 序列化,把Python类型转换为字符串,但是Python类型对象中的值如果是字符串的话,一定要用双引号。因为在Python中单引号和双引号我们可以不加区分的来使用,但在其他语言中就不是了,所以对象中的字符串一定要用双引号。

>>> s1 = {"name":"daniel","age":18}
>>> type(s1)
<class 'dict'>
>>> result = json.dumps(s1)
>>> type(result)
<class 'str'>

loads  反序列化, 把字符串转换成Python类型

>>> result
'{"name": "daniel", "age": 18}'
>>> type(result)
<class 'str'>
>>> result1 = json.loads(result)
>>> type(result1)
<class 'dict'>
>>> result1
{'name': 'daniel', 'age': 18}

dump  序列化,写入到文件中

>>> s1 = {"name":"daniel","Age":18}
>>> type(s1)
<class 'dict'>

>>> import json
>>> json.dump(s1,open('test.txt','w'))

#看下文件中的内容:
root@test3-ubunut:~# cat test.txt && echo
{"name": "daniel", "Age": 18}

load   反序列化,从文件中读取

>>> result = json.load(open('test.txt','r')
... )
>>> type(result)
<class 'dict'>
>>> result
{'name': 'daniel', 'Age': 18}

json 支持的python数据类型:

+---------------+-------------------+
| JSON | Python |
+===============+===================+
| object | dict |
+---------------+-------------------+
| array | list |
+---------------+-------------------+
| string | str |
+---------------+-------------------+
| number (int) | int |
+---------------+-------------------+
| number (real) | float |
+---------------+-------------------+
| true | True |
+---------------+-------------------+
| false | False |
+---------------+-------------------+
| null | None |
+---------------+-------------------+

 

一个实例,抓取天津的天气信息:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

import json
import requests

response = requests.get('http://wthrcdn.etouch.cn/weather_mini?city=天津')
response.encoding = 'utf-8'
result = json.loads(response.text)
print(result)


执行结果如下:
{'data': {'wendu': '22', 'aqi': '68', 'yesterday': {'fl': '微风', 'low': '低温 20℃', 'fx': '东南风', 'high': '高温 30℃', 'type': '多云', 'date': '6日星期一'}, 'ganmao': '相对今天出现了较大幅度降温,较易发生感冒,体质较弱的朋友请注意适当防护。', 'city': '天津', 'forecast': [{'fengli': '微风级', 'low': '低温 19℃', 'high': '高温 25℃', 'fengxiang': '东北风', 'type': '阵雨', 'date': '7日星期二'}, {'fengli': '微风级', 'low': '低温 21℃', 'high': '高温 32℃', 'fengxiang': '南风', 'type': '', 'date': '8日星期三'}, {'fengli': '3-4级', 'low': '低温 22℃', 'high': '高温 34℃', 'fengxiang': '南风', 'type': '多云', 'date': '9日星期四'}, {'fengli': '3-4级', 'low': '低温 21℃', 'high': '高温 33℃', 'fengxiang': '东南风', 'type': '雷阵雨', 'date': '10日星期五'}, {'fengli': '3-4级', 'low': '低温 19℃', 'high': '高温 28℃', 'fengxiang': '东北风', 'type': '多云', 'date': '11日星期六'}]}, 'desc': 'OK', 'status': 1000}
View Code

 

2. pickle

pickle模块使用的数据格式是Python专用的,并且不同的版本不向后兼容,其他语言也不能识别,如果要和其他语言交互,需要使用json模块。

主要参数:

dumps  序列化

>>> s1
{'name': 'daniel', 'Age': 18}
>>> type(s1)
<class 'dict'>
>>> result = pickle.dumps(s1)
>>> type(result)
<class 'bytes'>
#格式是pickle独有的,无法直接读取
>>> print(result)
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00danielq\x02X\x03\x00\x00\x00Ageq\x03K\x12u.'
View Code

loads   反序列化

>>> type(result)
<class 'bytes'>
>>> r = pickle.loads(result)
>>> print(r)
{'name': 'daniel', 'Age': 18}
>>> type(r)
<class 'dict'>
View Code

dump  序列化,将对象持久化到文件中

>>> s1
{'name': 'daniel', 'Age': 18}
>>> type(s1)
<class 'dict'>
#写入模式需要用二进制写入模式,否则报错
>>> pickle.dump(s1,open('test.txt','wb'))

#查看文件内容
root@test3-ubunut:~# cat test.txt &&echo
?}q(XnameqXdanielqXAgeqKu.

#是乱码,无法直接查看
View Code

load   反序列化,从文件中读取,并转换为原来的Python对象

>>> result = pickle.load(open('test.txt','rb'))
>>> type(result)
<class 'dict'>
>>> result
{'name': 'daniel', 'Age': 18}

#注意,读取时必须使用二进制读取模式,加b,否则会抛异常!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/codecs.py", line 319, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
View Code

 

七. time、datetime模块

1. time模块

time, python中一种管理时间和日期的模块,常用的操作有如下:

clock()  返回当前处理器时间

>>> import time
>>> time.clock()
0.333191

process_time()  返回处理器时间

>>> time.process_time()
0.336428017

time()  返回当前时间戳,从Unix元年(1970/1/1 0点)开始到此时此刻经过的秒数

>>> time.time()
1465288559.7881007

ctime()  返回当前时间,日期格式:'Tue Jun  7 16:36:24 2016'。也可以将时间戳转换为字符串格式。

>>> time.ctime()
'Tue Jun  7 16:36:24 2016'
>>> time.ctime(time.time())
'Tue Jun  7 16:51:45 2016'

gmtime()  返回struct_time 时间格式的当前时间, UTC时间。 也可以将时间戳转换为struct_time格式

>>> time.gmtime()
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=8, tm_min=36, tm_sec=57, tm_wday=1, tm_yday=159, tm_isdst=0)

>>> a = time.gmtime()
>>> type(a)
<class 'time.struct_time'>
View Code
struct_time即用数组的形式表示,共有9个元素,同一个时间戳的struct_time 会因为时区不同而不同;

tm_year     年
tm_mon=6  月
tm_mday=7 日
tm_hour=8  时
tm_min=36  分
tm_sec=57   秒
tm_wday=1  周,范围0-6,0是周一,依次类推
tm_yday=159  今天是今年中第几天,范围1-366
tm_isdst=0   是否是夏令时,范围:-1, 0  1
struct_time

localtime()   返回系统时间,struct_time格式。 可将时间戳转换为struct_time格式。

>>> time.localtime()
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=48, tm_sec=43, tm_wday=1, tm_yday=159, tm_isdst=0)
>>> time.time()
1465289371.3065152
#也可以将时间戳转换为struct_time格式
>>> time.localtime(time.time())
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=49, tm_sec=36, tm_wday=1, tm_yday=159, tm_isdst=0)
View Code

mktime()  与localtime功能相反,将struct_time 格式的时间转换为时间戳。

>>> time.mktime(time.localtime())
1465289600.0

sleep()   用过好多次了,让程序休眠, 默认单位秒

>>> time.sleep(1)
>>> time.sleep(0.3)

strftime()  将struct_time格式转换成指定的字符串格式

>>> time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
'2016-06-07 16:55:35'
>>> time.strftime('%Y/%m/%d %H%M%S',time.localtime())
'2016/06/07 165551'
View Code

strptime()  将字符串转换成struct格式

>>> type(a)
<class 'str'>
>>> a
'2016/06/07 165646'
>>> time.strptime(a,'%Y/%m/%d %H%M%S')
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=16, tm_min=56, tm_sec=46, tm_wday=1, tm_yday=159, tm_isdst=-1)
View Code

python中日期格式化符号

%y  两位数年(0-99)
%Y  四位数年(0000-9999)
%m 月,01-12
%d  天,0-31
%H  时, 0-23
%I   时,01-12,十二小时制
%M  分,  0-59
%S  秒, 0-59 

%a  本地简化星期名称
%A  本地完整星期名称
%b  本地简化月份名称
%B  本地完整月份名称
%c   本地相应的日期表示和时间表示
%j   年内的一天(01-366)
%p   本地AM或者PM等价符
%U   一年中的星期数(00-53),星期天为一星期的开始
%w   星期(0-6)
%W  一年中的星期数(0-53)  星期一为一星期的开始
%x   本地相应的日期表示
%X   本地相应的时间显示
%Z   当前时区名字
%%  %本身
>>> time.strftime('%x')
'06/07/16'
>>> time.strftime('%X')
'17:08:44'
>>> time.strftime('%Z')
'CST'
>>> time.strftime('%a')
'Tue'
>>> time.strftime('%A')
'Tuesday'
>>> time.strftime('%b')
'Jun'
>>> time.strftime('%c')
'Tue Jun  7 17:09:02 2016'
>>> time.strftime('%j')
'159'
>>> time.strftime('%p')
'PM'
>>> time.strftime('%U')
'23'
>>> time.strftime('%w')
'2'
>>> time.strftime('%W')
'23'
>>> time.strftime('%A')
'Tuesday'
View Code

2. datetime

datetime模块也是Python提供用于操作日期和时间的模块,datetime定义了下面几个类:

datetime.date   日期类,常用的有year、month、day等;

datetime.time   时间类,常用的属性有hour、minute、second、microsecond等;

datetime.datetimt   日期时间类

datetime.tzinfo   与时区相关的

date日期类:

date.max date.min date对象所能表示最大的时间和最小的时间

>>> datetime.date.max
datetime.date(9999, 12, 31)
>>> datetime.date.min
datetime.date(1, 1, 1)
View Code

date.today()  返回当前日期

>>> datetime.date.today()
datetime.date(2016, 6, 7)

date.fromtimestamp()  将时间戳转换为日期格式

>>> a = time.time()
>>> datetime.date.fromtimestamp(a)
datetime.date(2016, 6, 7)
View Code

date.replace(year, month, day) 生成一个新的日期对象,用指定的年、月、日替代原有对象中的属性,原有对象不变。

>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
>>> tomorrow = now.replace(day = 8)
>>> tomorrow
datetime.datetime(2016, 6, 8, 17, 25, 9, 730194)
View Code

date.timetuple()  返回日期对应的struct_time 对象;

>>> datetime.date.timetuple(now)
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=159, tm_isdst=-1)
View Code

date.weekday()   返回周几,范围0-6

>>> datetime.date.weekday(now)
1
View Code

date.isoweekday()  返回周几,范围1-7

>>> datetime.date.isoweekday(now)
2
>>> now
datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
View Code

date.isocalendar()   返回日期元组,包括(year, month,day) 

>>> datetime.date.isocalendar(now)
(2016, 23, 2)
View Code

date.isoformat()  返回格式YYYY-MM-DD的字符串

>>> datetime.date.isoformat(datetime.datetime.now())
'2016-06-07'
View Code

time时间类:

time类表示时间,由小时、分钟、秒以及毫秒组成。

time.min  time.max time类所能表示最小、最大的值;

>>> datetime.time.min
datetime.time(0, 0)
>>> datetime.time.max
datetime.time(23, 59, 59, 999999)
View Code

time.resolution  时间的最小单位,1微秒

>>> datetime.time.resolution
datetime.timedelta(0, 0, 1)
View Code
time类提供的实例方法和属性

datetime类

datetime类是date和time类的合并,包括他们俩的所有的信息。

datetime.min  datetimt.max  表示的最小值和最大值

>>> datetime.datetime.min
datetime.datetime(1, 1, 1, 0, 0)
>>> datetime.datetime.max
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
View Code

datetime.resolution  最小单位

>>> datetime.datetime.resolution
datetime.timedelta(0, 0, 1)
View Code

datetime.today()  返回当前本地时间

>>> datetime.datetime.today()
datetime.datetime(2016, 6, 7, 17, 44, 32, 339503)
View Code

datetime.now()  返回当前本地时间

>>> datetime.datetime.now()
datetime.datetime(2016, 6, 7, 17, 45, 21, 563377)
View Code

datetime.utcnow()  返回当前utc时间

>>> datetime.datetime.utcnow()
datetime.datetime(2016, 6, 7, 9, 46, 46, 155147)
View Code

datetime.fromtimestamp(timestamp)  将时间戳转换为datetime时间

>>> datetime.datetime.fromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 17, 47, 30, 571110)
View Code

datetime.utcfromtimestamp(timestamp)  将时间戳转换为utc时间

>>> datetime.datetime.utcfromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 9, 47, 53, 533692)
View Code

datetime.strptime(date,time)  根据date和time,将字符串转换成日期格式

>>> datetime.datetime.strptime('2016/06/07 17:50:00','%Y/%m/%d %H:%M:%S')
datetime.datetime(2016, 6, 7, 17, 50)
View Code
# 往后十天
>>> new_date = datetime.datetime.now() + datetime.timedelta(days=10)
>>> new_date
datetime.datetime(2016, 6, 17, 17, 54, 55, 709101)

#往前20天
>>> new_date = datetime.datetime.now() - datetime.timedelta(days=20)
>>> new_date
datetime.datetime(2016, 5, 18, 17, 55, 45, 566495)

#20小时前
>>> new_date = datetime.datetime.now() - datetime.timedelta(hours=20)
>>> new_date
datetime.datetime(2016, 6, 6, 21, 56, 24, 738440)

# 一周前
>>> new_date = datetime.datetime.now() - datetime.timedelta(days=7)
>>> new_date
datetime.datetime(2016, 5, 31, 17, 57, 45, 228619)

# 20秒之后
>>> new_date = datetime.datetime.now() + datetime.timedelta(seconds=20)
>>> new_date
datetime.datetime(2016, 6, 7, 18, 0, 32, 871318)
一些日期操作例子

 

八. logging模块

关于日期模块(logging)几个重要的概念:

  • Logger: 记录器,应用程序(代码)直接可以使用的接口。
  • Handle: 处理器,将记录器产生的日志记录发送给合适的目的地,主要有:屏幕(StreamHandler)、文件(FileHandler)、syslog(SyslogHandler)等,更多点我
  • Filter:    过滤器,提供良好的粒度控制,决定输出哪些日志记录。
  • Formatter: 格式化工厂,定义输出日志记录的格式。

日志级别:

  • DEBUG:   调试模式,记录详细信息,常用于调试程序。 数字值:10
  • INFO:    一般信息,程序运行的正常运行信息。 数字值:20
  • WARN:  警告级别,表示发生一些意外,或者将要发生意外,比如警告磁盘满了,但程序还是能工作。数字值:30
  • ERROR:  错误级别,比警告更严重,程序已经不能执行某些功能了。数字值:40
  • CRITICAL: 严重错误,比error更严重,程序已经不能工作了。 数字值:50

1. Logger记录器

Logger是一个树形层级结构,使用接口之前必须创建Logger实例,也就是创建一个记录器,如果没有显式的创建,则默认创建一个root Logger,并应用默认日志级别为Warn,处理器Handler(StramHandler),和格式化工程Formatter(默认格式是一个简单使用程序输出的格式)。

#创建方法:
import logging
logger = logging.getLogger('MyAPP')
import logging

logging.warning('warning msg')
logging.info('info msg')

#执行程序之后,显示:
WARNING:root:warning msg

#因为默认的输出日志级别是warn
简单例子

创建Logger后,就可以使用下面方法设置日志级别了,增加处理器Handler。

logger.setLevel(logging.INFO)  #设置日志级别为Info,也就是Info级别以上的才会输出,Debug不会输出

logger.addHandler('MyHandler')  #为Logger增加一个处理器

logger.removeFilter('Myhandler') #为Logger删除一个处理器

2. Handler处理器

Handler处理器有好多,我们这块只讨论StreamHandler(输出到console),FileHandler(输出到文件), 更多请参照Python官方介绍

创建Handler:

ch = logging.StreamHandler(stream=None)  
fh = logging.FileHandler('myapp.log',mode='a',encoding='utf-8')

创建完成Handler后,就可以设置日志级别,设置格式化Formatter,增加或者删除Filter了:

ch.setLevel(logging.INFO)   #设置日志级别
fh.setLevel(logging.WARN)   

ch.addFilter('MyAPP_filter')  #增加一个过滤器,可以增加多个
fh.addFilter('MyAPP_filter')
import logging
#记录到文件,并输出级别为DEBUG
logging.basicConfig(filename='test.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

#执行程序后,打开test.log文件,内容如下:
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too


#因为输出日志级别是debug,所以debug级别以上的信息全部会记录
记录到文件例子
import logging
logging.basicConfig(format='%(levelname)s:%(msg)s',level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')

#执行后显示:
DEBUG:debug message
INFO:info message
WARNING:warn message

#可以看到没有root了
改变现实消息格式
import logging
logging.basicConfig(format='%(asctime)s %(levelname)s:%(msg)s',level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')


#执行后显示:
2016-06-07 22:10:42,186 DEBUG:debug message
2016-06-07 22:10:42,187 INFO:info message
2016-06-07 22:10:42,187 WARNING:warn message

#默认显示上面所示的日期时间格式,提供给datefmt参数给basicConfig即可,如下:
import logging
logging.basicConfig(format='%(asctime)s %(levelname)s:%(msg)s',level=logging.DEBUG,datefmt='%Y/%m/%d %H:%M:%S')
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')


#执行后返回:
2016/06/07 22:14:16 DEBUG:debug message
2016/06/07 22:14:16 INFO:info message
2016/06/07 22:14:16 WARNING:warn message
在消息中显示日期/时间

3. Filter 过滤器

Handler和Logger可以使用Filter来完成比Level更复杂的过滤。filter基类只允许Logger层次以下的事件。

Filter决定哪些记录需要发给Handler,可以用在Handler和Logger上。

实例,将日志打印到分别打印到屏幕和文件中:

import logging

#先定义一个Logger
logger = logging.getLogger('MyApp')
logger.setLevel(logging.DEBUG)

#创建console,并设定日志级别为debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

#创建文件Handler
fh = logging.FileHandler('access.log')
fh.setLevel(logging.INFO)

#创建格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#而后添加formatter到ch和fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)

#添加Handler到Logger中
logger.addHandler(ch)
logger.addHandler(fh)

#应用日志
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
分别打印日志到屏幕和文件
2016-06-07 22:39:29,107 - MyApp - DEBUG - debug message
2016-06-07 22:39:29,108 - MyApp - INFO - info message
2016-06-07 22:39:29,108 - MyApp - WARNING - warn message
2016-06-07 22:39:29,108 - MyApp - ERROR - error message
2016-06-07 22:39:29,108 - MyApp - CRITICAL - critical message
执行程序屏幕输出内容
2016-06-07 22:39:29,108 - MyApp - INFO - info message
2016-06-07 22:39:29,108 - MyApp - WARNING - warn message
2016-06-07 22:39:29,108 - MyApp - ERROR - error message
2016-06-07 22:39:29,108 - MyApp - CRITICAL - critical message
access.log文件内容

4. Formatter

使用Formatter对象设置日志信息最后的规则,结构和内容,默认的时间格式是%Y-%m-%d %H:%M:%S。

formatter = logging.Formatter(fmt=None, datefmt=None)

fmt是消息格式化字符串,datefmt是日期字符串。如果指明fmt,将使用‘%(message)s'

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

5. Logging模块处理流程(图片来自官网)

  • 首先判断日志等级是否大于Logger等级,大于往下,否则结束;
  • 生成日志。首先判断是否异常,是,则添加异常信息。而后根据日志级别做一般格式化;
  • 使用注册到Logger中的Filter来过滤。如果有多个过滤器则依次过滤,只要有一个过滤器返回为假,则过滤结束,并且将该日志丢弃。不再处理,而处理流程也至此结束。否则,继续往下;
  • 在当前Logger中查找Handler,如果找不到,则往上到Logger的父Logger中找;如果找到一个或者多个Handler,则依次按Handler来处理。在每个Handler处理过程中,会首先判断日志等级是否大于该Handler的等级,如果大于,则往下执行,否则,处理流程结束。
  • 执行handler中的filter方法,该方法会依次执行注册该Handler中的Filter。如果有一个Filter判断该日志信息为假,则后面的所有Filter都不再继续执行,而直接将该日志信息丢弃,处理流程结束。
  • 使用Formatter格式化最终的日志结果。
  • 输出日志信息到目标(由Handler确定输入目标)。

  注: 以上文字信息摘抄自简书,感谢简书作者提供优质的教程。此处内容稍有改动,特此声明。

6. 日志格式

%(asctime)s       日志时间
%(created)f        打印时间戳
%(filename)s      返回文件名称,文件全名
%(funcName)s    打印当前函数
%(levelno)s        打印级别号码
%(levenname)s   日志级别名称
%(lineno)d          打印调用行号
%(module)s        打印模块
%(msecs)d          微秒
%(message)s       日志信息
%(name)s Logger  名称
%(pathname)s      日志文件的绝对路径
%(process)d          进程ID
%(processName)s  进程名
%(relativeCreated)d 运行时长
%(thread)d           线程ID
%(threadName)s    线程名

7. 日志配置

配置方式

  • 显式创建记录器Logger、处理器Handler和格式化Formatter,并进行设置;
  • 通过简单方式进行配置,使用 basicConfig() 函数来配置;
  • 通过配置文件进行配置,使用 fileConfig() 函数来读取配置文件;
  • 通过配置字典进行配置,使用 dictConfig() 函数读取配置信息;
  • 通过网络进行配置,使用 listen() 函数来进行网络配置。

basicConfig关键字参数

filename 创建一个Filehandler,使用文件名
filemode 如果指明了文件名,应该指定打开文件模式,如果不指定,默认为a追加模式。
format handler使用指明的格式化字符串
datefmt 使用指明的日期时间格式。
level 日志级别
stream 使用指明的流来初始化StreamHandler。这个参数与filename不兼容。如果两个都有,stream被忽略。

posted @ 2016-06-06 14:49  DBQ  阅读(1045)  评论(0编辑  收藏  举报