本节大纲:
一:双层装饰器:
一个函数可以被多层装饰器进行装饰,函数渲染(编译)从下到上,函数执行从上到下。
如下程序:
1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 # author:liumeide 4 5 # USERINOF={'islogin':True} 6 USERINOF={'user_type':'2','islogin':True} 7 def login(func): 8 def inner_1(*args,**kwargs): 9 if USERINOF.get('islogin',None): 10 ret=func() 11 return ret 12 else: 13 print('login first!') 14 return inner_1 15 def check_user(func): 16 def inner(*args,**kwargs): 17 if USERINOF.get('islogin',None)==True and USERINOF.get('user_type',None)=='2': 18 ret=func() 19 return ret 20 else: 21 print('permission deny!!') 22 return inner 23 @login 24 @check_user 25 def index(): 26 print('index') 27 28 index()
多层装饰器该如何理解呢?
上面的函数的可以执行顺序可以理解为:

首先:函数login、check_user、index加载到内存。在调用index函数的时候,执行index函数,因为index函数被check_user函数装饰,所以把check_user函数的内的inner函数体重新赋值给index,index函数体被当做
参数传入check_user函数。当满足inner函数的条件的时候被执行,新的函数index函数的函数体inner被login函数的所装饰,也就是说inner函数被重新赋值给inner_1函数。也就是说最后调用执行index()顺序:
先执行inner_1函数体----->在执行inner函数体-->在执行index原先的函数体。
多层装饰器以此类推,同样的原理。三层以上的装饰器很少使用。
二:字符串格式化:
%[(name)][flags][width].[precision]typecode
%号初始化字符串:
顺序传入参数。
执行名称传入参数
保留小数点后几位
当有占位符的时候 需要%%输出%如果没有占位符需要只写一个%在占位符的时候类似一个转义的意思。
1:
1 print('name:%s,age:%s'%('evil','22')) 2 3 name:evil,age:22
普通字符串的初始化,需要传入的实参和占位符的个数保持一致。
(name):按名字进行取值。
2:可以根据占位的name的key,根据后面的字典对应的value进行传入来进行传入实参。
1 print("Name:%(name)s"%{'name':'ok'}) 2 Name:ok
3: flags一般和width宽度来一起配合使用,如果单纯的使用flags并没什么效果。很少使用这个功能。
1 print("qqqq%(name)+s"%{'name':'ok'}) 2 qqqqok
2个一起配合使用:+10表示右对齐,字符串ok占用10个字符。右对齐;正数前加正好,负数前加负号
1 print("qqqq%(name)+10s"%{'name':'ok'}) 2 qqqq ok
-号表示左对齐。左对齐;正数前无符号,负数前加负号;
空格 右对齐;正数前加空格,负数前加负号;
0 右对齐;正数前无符号,负数前加负号;用0填充空白处;
.precision 可选,小数点后保留的位数
但是没有居中的功能。
1 print("qqqq%(name)-10sqqq"%{'name':'ok'}) 2 qqqqok qqq
print("%+10d" % 10) +10
1 print("%-10d" %10) 2 10
1 print("%04d" % 5) 2 0005
1 print("%04d" % -5) 2 -005
1 print("% 4d" % 5) 2 5
1 print("%.2f" % 1.225) 2 1.23
typecode表示执行传入的字符串类型。有如下:
s,获取传入对象的__str__方法的返回值,并将其格式化到指定位置
r,获取传入对象的__repr__方法的返回值,并将其格式化到指定位置
c,整数:将数字转换成其unicode对应的值,10进制范围为 0 <= i <= 1114111(py27则只支持0-255);字符:将字符添加到指定位置
o,将整数转换成 八 进制表示,并将其格式化到指定位置
x,将整数转换成十六进制表示,并将其格式化到指定位置
d,将整数、浮点数转换成 十 进制表示,并将其格式化到指定位置
e,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(小写e)
E,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(大写E)
f, 将整数、浮点数转换成浮点数表示,并将其格式化到指定位置(默认保留小数点后6位)
F,同上
g,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是e;)
G,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是E;)
%,当字符串中存在格式化标志时,需要用 %%表示一个百分号
但是没有将整数转换成二进制功能。
1 print('%%%d'%5) 2 %5
1 print('------%c-----%o-------%x'%(65,15,15)) 2 ------A-----17-------f
format函数初始化字符串:
[[fill]align][sign][#][0][width][,][.precision][type]
1 print('adad{0}adadada{0}dada{1}'.format(1,2)) 2 adad1adadada1dada2
1 print('adad{name}adadada{age}'.format(name='OK',age=22)) 2 adadOKadadada22
居中功能并用a填充宽度为10.
1 print('adad{name:a^10s}adadada{age}'.format(name='OK',age=22)) 2 adadaaaaOKaaaaadadada22
左对齐<
1 print('adad{name:a<10s}adadada{age}'.format(name='OK',age=22)) 2 adadOKaaaaaaaaadadada22
右对齐>
1 print('adad{name:a>10s}adadada{age}'.format(name='OK',age=22)) 2 adadaaaaaaaaOKadadada22
转化百分比
print('this is {:%}'.format(0.2222)) this is 22.220000%
支持整数转化二进制输出:
1 print("---{0:d}----{0:b}---{0:o}--{1:%}".format(15,0.2)) 2 ---15----1111---17--20.000000%
三:生成器和迭代器
具有生成一定条件数据的能力的对象叫做生成器。
具有取数据的能力叫做迭代器。
在进行处理大数据的时候,用生成器会降低内存的消耗,每一次取值生成一次,在下次取值的时候,内存会回收该值,避免了浪费内存。
其中filter函数和map函数返回的对象就是生成器。比如xrange函数返回的对象也是生成器。
1 a=[1,2,31,4] 2 f=filter(lambda a:a>3,a) 3 4 for i in f: 5 print(i) 6 print(f) 7 31 8 4 9 <filter object at 0x004C5B50>
生成器本身由函数创造的,如何把普通的函数转成生成器呢?
如果函数体内包含关键字yield 时这个函数就是生成器。
1 def fun(): 2 print(111) 3 yield 1 4 fun() 5 print(fun()) 6 <generator object fun at 0x00584780>
在调用函数的fun()时候并不执行函数。只有去取这个对象的时候进行生成。
1 def fun(): 2 yield 1 3 yield 2 4 yield 3 5 fun() 6 for i in fun(): 7 print('__%s'%i) 8 print(fun()) 9 __1 10 __2 11 __3 12 <generator object fun at 0x004E4780>
当for循环执行时,执行函数体。第一次的时候,去函数func找yield 对应的值1 赋值给i输出,第二次从第一次的位置下一个的yield的值为2取值并赋值给i,直到取到所有的yield的结束循环。每一次取值生成对应的对象。
上面的函数fun是生成器而生成的结果fun()是迭代器,也就说对象具有可以被迭代,在迭代时候执行next方法取值,只能从前往后取值,
练习:基于生成器生成range功能。
1 def myrange(args): 2 st=0 3 while True: 4 if st >args: 5 return 6 yield st 7 st+=1 8 ret=myrange(9) 9 for i in ret: 10 print(i) 11 0 12 1 13 2 14 3 15 4 16 5 17 6 18 7 19 8 20 9
四:函数递归。执行本身的函数体,当满足一定条件退出,并把最后执行的结果返回给上个函数,如果没有条件限制会无限执行下去。
1 def fun(x): 2 x+=1 3 if x>3: 4 return 1 5 return fun(x) 6 t=fun(1) 7 print(t) 8 1
执行3次fun(x)函数。循环执行多次相同的函数体。
练习:实现累乘功能,比如输入3 实现3*2*1
1 def fun(x): 2 if x==1: 3 return 1 4 return x*fun(x-1) 5 print(fun(3)) 6 6
五:模块
分类:内置模块、第三方模块、自定义模块。在python中叫模块其他语言叫类库。
使用模块:先import 导入后使用。
存在方式:.py文件。或者一个文件夹里有多个.py文件组成的一个模块。
自定义模块导入:
目录之间用.表示
如果同一级不通模块导入相同的方法该如何区分呢???
1:通过相对路径来区分
request模块调用上面的2个co模块。如果路径层级不多可以这么写 如果层级过多呢?调用次数过多呢?
1 import lib.co 2 import common.co 3 common.co.login() 4 lib.co.login() 5 common OK 6 lib OK
简单的方法 将导入的模块进行别名操作:
1 from lib import co as f1 2 from common import co as f2 3 f1.login() 4 f2.login() 5 lib OK 6 common OK
下面的方法是我们经常用的。
模块的意义:便于管理代码。不同的功能的代码进行归类。
模块导入的依据:
1 import sys 2 print(sys.path) 3 C:python2day6 4 C:UsersAdministratorAppDataLocalProgramsPythonPython35-32python35.zip 5 C:UsersAdministratorAppDataLocalProgramsPythonPython35-32DLLs 6 C:UsersAdministratorAppDataLocalProgramsPythonPython35-32lib 7 C:UsersAdministratorAppDataLocalProgramsPythonPython35-32 8 C:UsersAdministratorAppDataLocalProgramsPythonPython35-32libsite-packages
sys.path是一个路径的列表。默认python 有限搜索当前py文件的目录依次搜索。所以当我们自定义模块时候可以有2种方式让python 找到我们定义的模块:
1:把自定义模块加入这个列表(append())
2:把自定义的模块加入上面的目录之中。
这样在导入模块的时候不会出现:ImportError: No module named 'xxx'
那怎么找出自己模块所在目录呢?
1 import os 2 print(os.path.abspath(__file__)) 3 print(os.path.dirname(os.path.abspath(__file__))) 4 print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 5 6 C:python2day6s2.py 7 C:python2day6 8 C:python2
根据自己需求将自己的变量导入sys.path即可。
模块的名称:不要和系统的内置模块的名字冲突。
第三方模块:1:pip进行安装
2:源码进行安装。
安装第三方模块:requests
pip install requests
常用模块介绍:
json模块:
常用函数dums()、dum();loads()、load()
对于加s参数和不加s参数区别:
加s的是直接可以对python 的数据转换成json串(dums())或者直接将json串转换成python能识别的数据类型。而不加s的是将python的数据转换成json串并将json串写入文件中,或者将文件中的json串转换成python的基本数据。
1 data=[1,2,3,4,(1,2,True)] 2 obl=json.dumps(data) 3 with open('data.txt','w') as f1: 4 f1.write(obl) 5 <class 'str'> [1, 2, 3, 4, [1, 2, true]]
如果需要写入需要操作文件才能写入。
1 data=[1,2,3,4,(1,2,True)] 2 obl=json.dumps(data) 3 t_load=json.loads(obl,encoding='utf8') 4 print(type(t_load),t_load) 5 <class 'list'> [1, 2, 3, 4, [1, 2, True]]
在将python基本数据类型进行json转换的时候,会将python的一些数据类型或者相应的值进行转换,比如上面的python的元组转换json时候变为列表。True变为 true。
转换表如下:
跨语言平台常用该模块。该模块可以将python的基本数据类型(列表、字典)转换成字符串----序列化。也可以将字符串形式的python 类型字符创转换成对应的数据类型(列表、字典)--反序列化。
1 import json 2 list_1=[1,2,3,4,] 3 # json.dump(list_1,open('json.txt','w')) 4 obj=json.load(open('json.txt','r')) 5 print(obj) 6 [1, 2, 3, 4]
pickle模块:可以将非python数据类型的数据进行序列化和反序列化。存储方式是二进制。所以要以wb或者rb进行相关的dums和loads
1 import pickle 2 a='aa' 3 pickle.dump(bytes(a,encoding='utf-8'),open('pickle.txt','wb'))
pickle.dumps()将相应的数据类型转换成字节。如果不指定编码,python解释器会用默认编码进行处理。
1 import pickle 2 data=[1,2,3,4,(1,2,True)] 3 t=pickle.dumps(data) 4 print(t,type(t)) 5 b'x80x03]qx00(Kx01Kx02Kx03Kx04Kx01Kx02x88x87qx01e.' <class 'bytes'>
1 import pickle 2 a='aa' 3 # pickle.dump(bytes(a,encoding='utf-8'),open('pickle.txt','wb')) 4 t=pickle.load(open('pickle.txt','rb')) 5 s=str(t,encoding='utf-8') 6 print(s) 7 aa
import pickle data=[1,2,3,4,(1,2,True)] t=pickle.dumps(data) t_load=pickle.loads(t) print(t_load,type(t_load)) [1, 2, 3, 4, (1, 2, True)] <class 'list'>
在Django中会用到经常用到pickle模块。
requests模块:属于第三方模块需要进行手动安装(pip install requests)
进入python安装目录:E:python3.6Scripts
运行:pip.exe install request
import requests import json res=requests.get("http://wthrcdn.etouch.cn/weather_mini?city=北京") res.encoding='utf-8' print(type(res)) dict_1=json.loads(res.text) print(dict_1)
time模块和datatime 模块
获取本地准确时间:
1 import time
2 print( time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())))
3
4 2016-09-23 21:50:45
time模块:首先了解下
时间戳的概念:时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
1:获取本地时间戳
1 import time,datetime 2 print(time.time())##获取本地的时间的时间戳。 3 1465714711.3878157
2:获取本地时间。
1 import time,datetime 2 print(time.ctime()) 3 Sun Jun 12 15:02:33 2016
3:获取本地时间的时间struct_time格式。
1 print(time.localtime()) 2 time.struct_time(tm_year=2016, tm_mon=6, tm_mday=12, tm_hour=15, tm_min=27, tm_sec=2, tm_wday=6, tm_yday=164, tm_isdst=0)
4:将时间戳转换成字符串时间
1 import time,datetime 2 print(time.ctime(time.time())) 3 Sun Jun 12 15:05:13 2016
5:将时间戳转成struct_time
格式。time.mktime把struct_time格式的时间转换成时间戳。
1 print(time.mktime(time.gmtime())) 2 1465687430.0
struct_time格式是元组,可以进行相关取值。
这种获取的struct_time格式是hour不是本地地时间的hour,查时区的8小时。也就是说time.time()格林威治时间戳。换成time.localtime()
1 import time,datetime 2 print(time.gmtime(time.time())) 3 time.struct_time(tm_year=2016, tm_mon=6, tm_mday=12, tm_hour=7, tm_min=11, tm_sec=44, tm_wday=6, tm_yday=164, tm_isdst=0)
1 print(time.localtime()) 2 time.struct_time(tm_year=2016, tm_mon=6, tm_mday=12, tm_hour=16, tm_min=50, tm_sec=52, tm_wday=6, tm_yday=164, tm_isdst=0)
5:将struct_time格式转换成指定字符串时间格式
1 import time,datetime
2 print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime(time.time())))
3 2016-06-12 07:14:47
注意:也就是说首先需要获取时间戳然后转换成struct_time格式然后转换成你想要的时间格式。
可以把指定的时间格式字符串转换成struct_time格式然后在转换成时间戳。
1 print(time.strptime('2016-06-12 07:14:47',"%Y-%m-%d %H:%M:%S")) 2 time.struct_time(tm_year=2016, tm_mon=6, tm_mday=12, tm_hour=7, tm_min=14, tm_sec=47, tm_wday=6, tm_yday=164, tm_isdst=-1)
1 print(time.mktime(time.strptime('2016-06-12 07:14:47',"%Y-%m-%d %H:%M:%S"))) 2 1465686887.0
datetime模块:
返回当前时间格式xx-xx-xx
1 print(datetime.datetime.today()) 2 2016-06-12 15:25:21.323805
可以用切割获取我们想要的格式:
1 TIME=datetime.datetime.today()
2 TIME_INT=str(TIME).split('.')[0]
3 print(TIME_INT)
4 2016-06-12 16:40:00
将时间戳转换成时间字符串
1 print(datetime.datetime.fromtimestamp(time.time())) 2 2016-06-12 15:49:05.047899
输出当前时间:
1 print(datetime.datetime.now()) 2 2016-06-12 16:43:07.611796
也可以将时间格式的转换成字符换进行进行切割出大约的时间(因为毫秒去掉)
将时间戳转换成struct_time格式 如下输出不存在时区问题。
1 TIME=datetime.datetime.now() 2 print(TIME.timetuple()) 3 time.struct_time(tm_year=2016, tm_mon=6, tm_mday=12, tm_hour=16, tm_min=46, tm_sec=35, tm_wday=6, tm_yday=164, tm_isdst=-1)
将字符串转化成日期格式。
1 str_to_date = datetime.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") 2 2006-11-21 16:30:00
时间的加减:
1 new_date = datetime.datetime.now() + datetime.timedelta(days=10)##比现在时间加10天。 2 print(new_date) 3 2016-06-22 17:29:11.435878
1 new_date = datetime.datetime.now() + datetime.timedelta(days=-10)## 比现在减10天。 2 print(new_date) 3 2016-06-02 17:30:33.124046
1 new_date = datetime.datetime.now() + datetime.timedelta(hours=-10)#比现在减10小时。 2 print(new_date)
1 new_date = datetime.datetime.now() + datetime.timedelta(seconds=120)#比现在加120秒。 2 print(new_date) 3 2016-06-12 17:36:07.311463
模块:logging
首先创建logging实例。然后创建handler,分两种 一种输出到文件中 Filehandler,一种输出到终端的Streamhandler.分别为这两种handler指定输出格式setformatter
然后将这2种handler添加(add)到logging实例中。该实例就有输出到文件和终端的日志信息的属性。
实际例子:
1 import logging 2 def logsystem(x): 3 log=logging.getLogger("mylog")##定义一个logging的实例log 4 log.setLevel(logging.INFO) #设置全局日志级别,如果handler的日志级别低于INFO,不会输出日志信息。 5 6 fl=logging.FileHandler(x,encoding='utf-8')##设置一个文件输出的Filehandler.可以指定输出文件的编码,写入方式默认a追加,可以指定。 7 fl.setLevel(logging.ERROR)#设置输出级别ERROR 8 9 con=logging.StreamHandler()#设置终端Streamhandler. 10 con.setLevel(logging.INFO)#设置输出日志信息级别。 11 12 Formate=logging.Formatter(('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))#设置输出日志格式。主要asctime是输出时间。name是上面logging实例名字"mylog"。message是在调用的时候输入的日志信息。 13 fl.setFormatter(Formate)#分别给各个handler设置日志格式。 14 con.setFormatter(Formate) 15 16 log.addHandler(fl)#给logging实例log添加handler属性。 17 log.addHandler(con) 18 return log#该函数的返回值是logging实例。
1: logging.getLogger([name])
返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和log实例是一一对应的。这意味着,无需把logger实例在各个模块中传递。
只要知道name,就能得到 同一个logger实例
2:Log.setLevel(lvl)
设置logger的level, level有以下几个级别:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出
1 log.debug("foobar") # 不输出 2 log.info("foobar") # 输出 3 log.warning("foobar") # 输出 4 log.error("foobar") # 输出 5 log.critical("foobar") # 输出
3:Log.addHandler(hdlr)
log可以雇佣handler来帮它处理日志, handler主要有以下几种:
StreamHandler: 输出到控制台
FileHandler: 输出到文件
handler还可以设置自己的level以及输出格式。
logging.basicConfig([**kwargs])
* 这个函数用来配置root logger, 为root logger创建一个StreamHandler, 设置默认的格式。
* 这些函数: logging.debug()、logging.info()、logging.warning()、
logging.error()、logging.critical() 如果调用的时候发现root logger没有任何
handler, 会自动调用basicConfig添加一个handler
* 如果root logger已有handler, 这个函数不做任何事情
使用basicConfig来配置root logger的输出格式和level:
1 import logging 2 logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) 3 logging.debug('This message should appear on the console')
完整的例子:
1 class Logger: 2 ''' 3 功能:该类主要是日志实例的创建。 4 :param:filename:文件名字。 5 :param:logger:创建日志实例的名字。注意这个是变量。而且必须要有的变量。否则日志文件内容会乱。 6 :return:Log返回一个创建的log的实例。 7 ''' 8 def __init__(self,filename,logger): 9 self.filename=filename 10 self.logger=logger 11 def log_in(self): 12 Log=logging.getLogger(self.logger) 13 Log.setLevel(logging.INFO) 14 f1=logging.FileHandler(os.path.join(LOG_DIR,self.filename),encoding='utf-8') 15 f1.setLevel(logging.INFO) 16 formate=logging.Formatter(('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 17 f1.setFormatter(formate) 18 Log.addHandler(f1) 19 return Log