【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)
或者自己定义一个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'"
%c 单个字符, 将数字转换为unicode对应的值(还记得上个文章说的ASCII表么?),10进制范围为0 <=i <= 1114111
>>> print('%c' %a) A >>> a = 66 >>> print('%c' %a) B >>> a = 89 >>> print('%c' %a) Y
%d 十进制整数
>>> a = 20 >>> print('%d'%a) 20 >>> print('%d'%a) 20 >>> a = 25 >>> print('%d'%a) 25
%i 十进制整数
>>> print('%i'%a) 25 >>> a = 89 >>> print('%i'%a) 89 >>> a = 8901 >>> print('%i'%a) 8901
%o 八进制整数, 将整数转换为8进制表示
>>> a = 8901 >>> print('%o'%a) 21305 >>> a = 10 >>> print('%o'%a) 12
%x 十六进制整数, 将整数转换为16进制表示
>>> a = 10 >>> print('%x'%a) a >>> a = 15 >>> print('%x'%a) f >>> a = 12330 >>> print('%x'%a) 302a
%e 指数(小e)
>>> a = 12330 >>> print('%e'%a) 1.233000e+04
%E 指数(大写E)
>>> a = 12330 >>> print('%E'%a) 1.233000E+04
%f 浮点数,默认显示到小数点后6位
>>> a = 3.1415926 >>> print('%f'%a) 3.141593
%F 浮点数,默认显示到小数点后6位
>>> a = 3.1415926 >>> print('%F'%a) 3.141593
%g 指数e或浮点数(根据长度显示)
>>> a = 1300882 >>> print('%g'%a) 1.30088e+06
%G 指数E或浮点数(根据长度显示)
>>> a = 1300882 >>> print('%G'%a) 1.30088E+06
注意,要想在%方式格式化字符串中输出%,必须要使用%%来转译!
>>> 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
2. format方式
format格式是比较新的字符串格式化方式,大有替代%s的方式,不过目前官方没有明确表示要替换掉%s. 相比%s方式,format更加强大;
格式:
[[fill]align][sign][#][0][width][,][.precision][type]
- fill(可选项) 空白处填充的字符,需要配合width使用
-
>>> print('{:#^30} balabala'.format('tianchong')) ##########tianchong########### balabala
- 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
- sign(可选项) 有无符号数字
+ 正加正、负加负
- 正不变、负加负
空格 正好空格, 负加负
#+, 正加正,负加负 >>> print('{:#^+30}'.format(600)) #############+600############# #-, 正不变,负加负 >>> print('{:#^-30}'.format(600)) #############600############## #空格, 正加空格,负加负 >>> print('{:#^ 30}'.format(600)) ############# 600#############
- #(可选项) 对二进制、八进制、十六进制,加上后会显示 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
- , (可选项) 为数字添加分隔符, 如财务计算金钱,或者银行账户显示 1,322,220,220
-
>>> print('{:,d}'.format(203301010293)) 203,301,010,293
- width(可选项) 宽度
-
#一般都是配合fill、align和sign一起使用 >>> print('{:10,d}'.format(203301010293)) 203,301,010,293
- .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
- 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
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
使用这些模块是,先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...
- 源码安装:
先下载源码到目录,而后解压:
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__']
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
将文件保存到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目录下
一般情况下,都将第三方的模块放到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.
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' #找不到模块的
那么,我们这么试下,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}
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.'
loads 反序列化
>>> type(result) <class 'bytes'> >>> r = pickle.loads(result) >>> print(r) {'name': 'daniel', 'Age': 18} >>> type(r) <class 'dict'>
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. #是乱码,无法直接查看
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
七. 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'>
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
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)
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'
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)
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'
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)
date.today() 返回当前日期
>>> datetime.date.today()
datetime.date(2016, 6, 7)
date.fromtimestamp() 将时间戳转换为日期格式
>>> a = time.time() >>> datetime.date.fromtimestamp(a) datetime.date(2016, 6, 7)
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)
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)
date.weekday() 返回周几,范围0-6
>>> datetime.date.weekday(now)
1
date.isoweekday() 返回周几,范围1-7
>>> datetime.date.isoweekday(now) 2 >>> now datetime.datetime(2016, 6, 7, 17, 25, 9, 730194)
date.isocalendar() 返回日期元组,包括(year, month,day)
>>> datetime.date.isocalendar(now)
(2016, 23, 2)
date.isoformat() 返回格式YYYY-MM-DD的字符串
>>> datetime.date.isoformat(datetime.datetime.now()) '2016-06-07'
time时间类:
time类表示时间,由小时、分钟、秒以及毫秒组成。
time.min time.max time类所能表示最小、最大的值;
>>> datetime.time.min datetime.time(0, 0) >>> datetime.time.max datetime.time(23, 59, 59, 999999)
time.resolution 时间的最小单位,1微秒
>>> datetime.time.resolution
datetime.timedelta(0, 0, 1)
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)
datetime.resolution 最小单位
>>> datetime.datetime.resolution
datetime.timedelta(0, 0, 1)
datetime.today() 返回当前本地时间
>>> datetime.datetime.today()
datetime.datetime(2016, 6, 7, 17, 44, 32, 339503)
datetime.now() 返回当前本地时间
>>> datetime.datetime.now()
datetime.datetime(2016, 6, 7, 17, 45, 21, 563377)
datetime.utcnow() 返回当前utc时间
>>> datetime.datetime.utcnow()
datetime.datetime(2016, 6, 7, 9, 46, 46, 155147)
datetime.fromtimestamp(timestamp) 将时间戳转换为datetime时间
>>> datetime.datetime.fromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 17, 47, 30, 571110)
datetime.utcfromtimestamp(timestamp) 将时间戳转换为utc时间
>>> datetime.datetime.utcfromtimestamp(time.time())
datetime.datetime(2016, 6, 7, 9, 47, 53, 533692)
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)
# 往后十天 >>> 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
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被忽略。