0.本周知识点预览
装饰器进阶
字符串格式化 - format
生成器
递归
模块
json、pickle
time、datetime、logging
1.装饰器进阶 -- 双层装饰器
先直接上代码,咱们在来分析。
1 USER_INFO ={}2
3 defcheck_login(func):4 def inner(*args,**kwargs):5 if USER_INFO.get("is_login",None): 第一步6 ret = func(*args,**kwargs) 第二步7 returnret 第七步8 else:9 print("请登录")10 returninner11
12
13 defcheck_admin(func):14 def inner(*args,**kwargs):15 if USER_INFO.get("user_type",None) == 2: 第三步16 ret = func(*args,**kwargs) 第四步17 returnret 第六步18 else:19 print("无权查看")20 returninner21
22
23 deflogin():24 userinput = input("请输入用户名:")25 pwdinput = input("请输入密码:")26 if userinput == "lk" and pwdinput == "123":27 USER_INFO["is_login"] =True28 elif userinput == "liukai" and pwdinput == "234":29 USER_INFO["is_login"] =True30 USER_INFO["user_type"] = 2
31 index()32
33
34 @check_login35 @check_admin36 def index(*args,**kwargs): 第五步37 print("哈喽,管理员大大")38
39
40 defmain():41 whileTrue:42 userinput = input("请输入:1,管理;2,登陆:")43 userinput =int(userinput)44 if userinput == 1:45 index()46 elif userinput == 2:47 login()48
49 main()
执行结果如下:
1 请输入:1,管理;2,登陆: 1
2 请登录3 请输入:1,管理;2,登陆: 2
4 请输入用户名: lk5 请输入密码: 123
6 无权查看7 请输入:1,管理;2,登陆: 2
8 请输入用户名: liukai9 请输入密码: 234
10 哈喽,管理员大大
程序剖析:双层装饰器或者更多层装饰器,它的执行顺序为从上到下,而加载顺序为从下到上。
执行顺序:当选择登陆用户为liukai,密码为234时,这时USERINFO["is_login"]为True,USERINFO["user_type"]为2,当执行完login()函数,即将执行index()函数时,发现有check_admin装饰器,便加载到内存,又发现有check_login装饰器,也加载到内存。check_login函数已经是最外层的装饰器,便开始执行第一步。因为USERINFO["is_login"]为True,执行第二步,执行check_login()函数内的inner()函数内的func()函数,这个func函数就相当于装饰器check_admin函数,到了第三步,当执行check_admin的内部函数inner()函数,USERINFO["user_type"]为2,便执行func()函数,这个func函数就相当于index()函数。便执行了index的print()方法。
2.字符串格式化之 -- format
1 s1 = "i am {},age is {},name is {}".format("PE",25,"lk")2 s2 = "i am {},age is {},name is {}".format(*["PE",25,"lk"])3 s3 = "i am {0},age is {1},name is {0}".format("PE",25,"lk")4 s4 = "i am {0},age is {1},name is {0}".format(*["PE",25,"lk"])5 s5 = "i am {gz},age is {age},name is {name}".format(gz="PE",age=25,name="lk")6 s6 = "i am {gz},age is {age},name is {name}".format(**{"gz":"PE","age":25,"name":"lk"})7 s7 = "i am {:s},age is {:d},money is {:f}".format("lk",25,2345.67)8 s8 = "i am {:s},age is {:d},money is {:f}".format(*["lk",25,2345.67])9 s9 = "i am {name:s},age is {age:d}".format(name="lk",age=25)10 s10 = "i am {name:s},age is {age:d}".format(**{"name":"lk","age":25})11 s11 = "format: {:b},{:o},{:x},{:X},{:%}".format(58,58,58,58,58)12 s12 = "format: {0:b},{0:o},{0:x},{0:X},{0:%}".format(26)13
14 print(s1)15 print(s2)16 print(s3)17 print(s4)18 print(s5)19 print(s6)20 print(s7)21 print(s8)22 print(s9)23 print(s10)24 print(s11)25 print(s12)
代码执行如下:
1 i am PE,age is 25,name islk2 i am PE,age is 25,name islk3 i am PE,age is 25,name isPE4 i am PE,age is 25,name isPE5 i am PE,age is 25,name islk6 i am PE,age is 25,name islk7 i am lk,age is 25,money is 2345.670000
8 i am lk,age is 25,money is 2345.670000
9 i am lk,age is 25
10 i am lk,age is 25
11 format: 111010,72,3a,3A,5800.000000%
12 format: 11010,32,1a,1A,2600.000000%
程序剖析:
s1:每个大括号对应一个format()函数中的值。
s2:每个大括号对应一个format()函数的值,并且参数可以传入一个列表,注意要在列表前边加*
s3:每个大括号可以根据format()函数的位置传入,这个是%s、%d这种格式化所不具备的。
s4:每个大括号可以根据format()函数的位置传入,并且参数可以传入一个列表,注意要在列表前边加*
s5:每个大括号可以根据参数名按照指定位置传入。
s6:每个大括号可以根据参数名并且可以是字典的形式按照指定位置传入,注意字典之前加**
s7:每个大括号可以类似%s、%d的形式格式化,不过格式为{:s}、{:d}
s8:每个大括号可以类似%s、%d的形式格式化,不过格式为{:s}、{:d},并且参数可以传入一个列表,注意要在列表前边加*
s9: 每个大括号可以类似%s、%d的形式格式化,不过格式为{:s}、{:d},参数名按照指定位置传入。
s10:每个大括号可以类似%s、%d的形式格式化,不过格式为{:s}、{:d},根据参数名并且可以是字典的形式按照指定位置传入,注意字典之前加**
s11:每个大括号可以根据所写的格式转成想要的数字类型。b : 二进制;o:八进制;x:十六进制;X:十六进制
s12:可以把一个参数重复赋值给0这个位置参数。
特别提示:
s13 = "FORMAT:{:*^10s}".format("lk")
执行结果如下:
FORMAT:****lk****
代码剖析:^ 代表居中,^后边的10代表占几个字符,^前边的*是字符被占用后,剩余的(10-len(s))长度的字符用什么填充,这个是format特有的!!!
3.初识生成器
1.range()和xrange()
先了解下range()和xrange()函数的区别
python3.5里已经没有了xrange()函数。所以回到python2.7看下两个函数的差别
liukai@bogon:~$ python
Python2.7.10 (default, Jul 14 2015, 19:46:27)
[GCC4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type"help", "copyright", "credits" or "license" formore information.>>> range(10)
[0,1, 2, 3, 4, 5, 6, 7, 8, 9]>>> xrange(10)
xrange(10)
代码剖析:这里能看到,range(10)直接返回0到9的数字,而xrange(10)是一个能够生成0到9的数字的函数,可以用for循环打印出来。这就类似一个迭代器。
所以,当一个函数调用后返回一个迭代器,那么这个函数就是生成器。
2.yield
1 deffunc():2 print("start")3 yield 1
4 yield 2
5 yield 3
6
7 ret =func()8 print(type(ret))9
10 r1 = ret.__next__()11 print(r1)12
13 r2 = ret.__next__()14 print(r2)15
16 r3 = ret.__next__()17 print(r3)
执行结果如下:
start1
2
3
代码剖析:这个函数的返回是一个迭代器,因为有yield的存在,所以这也是一个生成器。一个生成器就可以被迭代,当执行__next__()函数就会把其中的元素都迭代打印出来。
4.初识递归
1 deffunc(num):2 num += 1
3 if num > 4:4 return "END {:d}".format(num)5 else:6 returnfunc(num)7
8 ret = func(1)9 print(ret)
执行结果如下:
END 5
程序剖析:func()函数在函数体内也调用func()本身,这样的调用方式,我们称为递归。
递归算法的特点:
1.递归就是直接或者间接调用函数本身。
2.递归时要定义一个调用的尽头,防止无限循环。
我们可以举个例子,来利用下递归完成一些简单的操作:
例如,计算一个数的阶乘操作就非常适合利用递归算法
1 deffunc(num):2 if num == 1:3 return 1
4 else:5 return num * func(num-1)6
7
8 result = func(5)9 print(result)
程序剖析:当执行func(5)时,num等于5,调用func()函数,会return 5 * func(4);func(4)会return 4 * func(3);func(3)会return 3 * func(2);func(2)return 2 * func(1);func(1)会return 1;最终result = func(5) 就相当于 5 * 4 * 3 * 2 * 1 。这就实现了阶乘。
5.初识模块
1.定义模块
liukai@bogon:~/PycharmProjects/s13/day5$ cat testlib/s2.py#!/usr/bin/env python3#-*- coding=utf-8 -*-#Author : LiuKai
deffuck():print("你瞅啥")defs2(num):print(num**3)
liukai@bogon:~/PycharmProjects/s13/day5$ cat s3/login.py#!/usr/bin/env python3#-*- coding=utf-8 -*-#Author : LiuKai
def xx(): print("s3.login")
liukai@bogon:~/PycharmProjects/s13/day5$ cat s4/login.py#!/usr/bin/env python3#-*- coding=utf-8 -*-#Author : LiuKai
def oo():print("s4.login")
注:每个模块为一个文件。
2.调用模块
当调用上述testlib目录中的s2模块时,有以下两种方式:
importtestlib.s2
testlib.s2.fuck()from testlib imports2
s2.fuck()
执行结果如下:
你瞅啥
你瞅啥
注:建议用第二种方式调用。
当调用s3、s4中的login模块时,因为login模块重名,可以给取个别名,如:
from s3 importlogin as s3_loginfrom s4 importlogin as s4_login
s3_login.xx()
s4_login.oo()
执行结果如下:
s3.login
s4.login
注:当调用的模块和系统或第三方模块重名时,强烈要求更改模块名,不要和自带的模块名一样!!!
6.json与pickle
1.初识json
1.loads()和dumps()
importjson
a='{"name":"lk"}'ret=json.loads(a)print(ret,type(ret))
b=[1,2,3,4,{"name":"liukai","age":123}]
result=json.dumps(b)print(result,type(result))importrequestsimportjson
response= requests.get('http://wthrcdn.etouch.cn/weather_mini?city=北京')
response.encoding= 'utf-8'ret=response.text
dic=json.loads(ret)print(dic,type(dic))
执行结果如下:
{'name': 'lk'} [1, 2, 3, 4, {"age": 123, "name": "liukai"}] {'data': {'aqi': '89', 'wendu': '21', 'ganmao': '各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。', 'forecast': [{'type': '多云', 'fengli': '微风级', 'fengxiang': '无持续风向', 'low': '低温 16℃', 'high': '高温 26℃', 'date': '7日星期二'}, {'type': '多云', 'fengli': '微风级', 'fengxiang': '无持续风向', 'low': '低温 19℃', 'high': '高温 31℃', 'date': '8日星期三'}, {'type': '多云', 'fengli': '3-4级', 'fengxiang': '南风', 'low': '低温 21℃', 'high': '高温 32℃', 'date': '9日星期四'}, {'type': '雷阵雨', 'fengli': '微风级', 'fengxiang': '无持续风向', 'low': '低温 20℃', 'high': '高温 30℃', 'date': '10日星期五'}, {'type': '多云', 'fengli': '微风级', 'fengxiang': '无持续风向', 'low': '低温 21℃', 'high': '高温 31℃', 'date': '11日星期六'}], 'yesterday': {'type': '多云', 'fl': '微风', 'date': '6日星期一', 'fx': '无持续风向', 'low': '低温 20℃', 'high': '高温 31℃'}, 'city': '北京'}, 'desc': 'OK', 'status': 1000}
程序剖析:json模块可以转换python的基本数据类型和字符串,loads()函数,可以把字符串转换为python的基本数据类型,如上述例子把字符串转换为字典。dumps()函数可以把python基本数据类型转换为字符串。这个在web页面上很常用,上述例子就是访问一个天气接口,拿到的数据是一个类似字典的字符串,这时可以利用json的loads()函数使其转换为真正的字典,方便我们进行后续的处理。
2.load()和dump()
importjson
a=[1,2,3]
ret1= json.dump(a,open("testjson.conf","w"))
ret2= json.load(open("testjson.conf","r"))
with open("testjson.conf","r") as f:print(f.read())print(ret2,type(ret2))
执行结果如下:
[1, 2, 3]
[1, 2, 3]
程序剖析:json的load()和dump()函数,与上述讲的loads()、dumps()函数很类似,都是在python基本数据类型和字符串之间转换,不同的是,load()、dump()函数是解析到文件中,并从文件中解析。
2.pickle
importpickle
li= [1,2,3]
li2= '[2,3,5]'r=pickle.dumps(li)
r2=pickle.loads(r)print(r)print(r2)
ret=pickle.loads(r)print(ret)
pickle.dump(li,open("testpickle.conf","wb"))
a= pickle.load(open("testpickle.conf","rb"))print(a)
执行结果如下:
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
注:pickle与json的相同点:都有load()、loads()、dump()、dumps()函数,功能与json的都相同
pickle与json的不同点:json适合大多数语言,pickle只适用于python;json只能序列化几种基本数据类型,pickle能对所有python数据类型序列化。
7.time模块、datetime模块、logging模块
1.time模块
importtimeprint(time.clock()) #返回处理器时间,3.3开始已废弃
print(time.process_time()) #返回处理器时间,3.3开始已废弃
print(time.time()) #返回当前系统时间戳
print(time.ctime()) #输出Tue Jan 26 18:23:48 2016 ,当前系统时间
print(time.ctime(time.time()-86640)) #将时间戳转为字符串格式
print(time.gmtime(time.time()-86640)) #将时间戳转换成struct_time格式
print(time.localtime(time.time()-86640)) #将时间戳转换成struct_time格式,但返回 的本地时间
print(time.mktime(time.localtime())) #与time.localtime()功能相反,将struct_time格式转回成时间戳格式#time.sleep(4) #sleep
print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将struct_time格式转成指定的字符串格式
print(time.strptime("2016-01-28","%Y-%m-%d") ) #将字符串格式转换成struct_time格式
执行结果如下:
0.04174
0.041797
1465308244.621358Tue Jun7 22:04:04 2016Mon Jun6 22:00:04 2016time.struct_time(tm_year=2016, tm_mon=6, tm_mday=6, tm_hour=14, tm_min=0, tm_sec=4, tm_wday=0, tm_yday=158, tm_isdst=0)
time.struct_time(tm_year=2016, tm_mon=6, tm_mday=6, tm_hour=22, tm_min=0, tm_sec=4, tm_wday=0, tm_yday=158, tm_isdst=0)1465308244.0
2016-06-07 14:04:04time.struct_time(tm_year=2016, tm_mon=1, tm_mday=28, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=28, tm_isdst=-1)
2.datetime模块
importtimeimportdatetimeprint(datetime.date.today()) #输出格式 2016-01-26
print(datetime.date.fromtimestamp(time.time()-864400) ) #2016-01-16 将时间戳转成日期格式
current_time = datetime.datetime.now() #print(current_time) #输出2016-01-26 19:04:30.335935
print(current_time.timetuple()) #返回struct_time格式
#datetime.replace([year[, month[, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]]]])
print(current_time.replace(2014,9,12)) #输出2014-09-12 19:06:24.074900,返回当前时间,但指定的值将被替换
str_to_date= datetime.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") #将字符串转换成日期格式
new_date = datetime.datetime.now() + datetime.timedelta(days=10) #比现在加10天
new_date = datetime.datetime.now() + datetime.timedelta(days=-10) #比现在减10天
new_date = datetime.datetime.now() + datetime.timedelta(hours=-10) #比现在减10小时
new_date = datetime.datetime.now() + datetime.timedelta(seconds=120) #比现在+120s
print(new_date)
执行结果如下:
2016-06-07
2016-05-28
2016-06-07 22:04:54.635981time.struct_time(tm_year=2016, tm_mon=6, tm_mday=7, tm_hour=22, tm_min=4, tm_sec=54, tm_wday=1, tm_yday=159, tm_isdst=-1)2014-09-12 22:04:54.635981
2016-06-07 22:06:54.657834
3.logging模块
importlogging
logger= logging.getLogger("lk-log")
logger.setLevel(logging.INFO)##设置一个全局日志级别INFO
ch= logging.StreamHandler() ##创建一个hander:将日志打印到屏幕上
ch.setLevel(logging.DEBUG) ##设置日志级别为DEBUG
fh= logging.FileHandler("warning.log") ##创建一个hander:将日志输出到文件
fh.setLevel(logging.WARNING) ##设置日志级别为WARNING
fh2= logging.FileHandler("error.log") ##创建一个hander:讲日志输出到文件
fh2.setLevel(logging.ERROR) ##设置日志级别为ERROR
format_for_stream= logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ##定义一个日志格式
format_for_file = logging.Formatter('%(asctime)s - - %(levelname)s - %(message)s') ##定义一个日志格式
ch.setFormatter(format_for_stream)##把日志格式添加到自定义的hander上
fh.setFormatter(format_for_file)
fh2.setFormatter(format_for_file)
logger.addHandler(ch)##把自定义的hander加到logger中.
logger.addHandler(fh)
logger.addHandler(fh2)
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
with open("warning.log","r") as file1:print(file1.read())print("=========================")
with open("error.log","r") as file2:print(file2.read())
执行结果如下:
2016-06-07 22:26:32,368 - lk-log - INFO -info message2016-06-07 22:26:32,369 - lk-log - WARNING -warning message2016-06-07 22:26:32,369 - lk-log - ERROR -error message2016-06-07 22:26:32,369 - lk-log - CRITICAL -critical message2016-06-07 22:26:32,369 - - WARNING -warning message2016-06-07 22:26:32,369 - - ERROR -error message2016-06-07 22:26:32,369 - - CRITICAL -critical message=========================
2016-06-07 22:26:32,369 - - ERROR -error message2016-06-07 22:26:32,369 - - CRITICAL - critical message
注:设置的全局日志级别是日志的最低级别,所以,要打印在屏幕的DEBUG级别的日志并没有打印。