一、双层装饰器
1、双层装饰器改良版
# 渲染的时候从下往上,执行的时候从上往下 USER_INFO = {} def check_login(func): # 第一层装饰器 def inner(*args, **kwargs): if USER_INFO.get('is_login', None): # get:如果查找的键不在字典中,默认返回None ret = func(*args, **kwargs) return ret else: print('请先登陆') return inner def check_admin(func): # 第二层装饰器 def inner(*args, **kwargs): if USER_INFO.get('user_type', None) == 2: ret = func(*args, **kwargs) return ret else: print('无查看权限') return inner @ check_login # 双层装饰器 @check_admin def index(): # 管理员用户 print('Index') @check_login def home(): # 普通用户 print('home') def login(): user = input('请输入用户名:') password = input('请输入密码:') if user == 'cjj' and password == '123': print('登录成功!') USER_INFO['is_login'] = True USER_INFO['user_type'] = 2 else: USER_INFO['is_login'] = True USER_INFO['user_type'] = 1 def main(): while True: # 循环语句 inp = input('1:登陆;2:查看信息;3:超级管理员管理\n>>>') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main()
2、双层装饰器实现用户登陆和权限管理
USER_INFO = {} def check_login(func): # 第一层装饰器 def inner(*args, **kwargs): if USER_INFO.get('is_login', None): # get:如果查找的键不在字典中,默认返回None ret = func(*args, **kwargs) return ret else: print('请先登陆') return inner def check_admin(func): # 第二层装饰器 def inner(*args, **kwargs): if USER_INFO.get('is_login', None): if USER_INFO.get('user_type', None) == 2: ret = func(*args, **kwargs) return ret else: print('无查看权限') else: print('请先登陆') return inner @check_admin def index(): print('Index') @check_login def home(): print('home') def login(): user = input('请输入用户名:') password = input('请输入密码:') if user == 'cjj' and password == '123': print('登录成功!') USER_INFO['is_login'] = True USER_INFO['user_type'] = 2 else: USER_INFO['is_login'] = True USER_INFO['user_type'] = 1 def main(): while True: # 循环语句 inp = input('1:登陆;2:查看信息;3:超级管理员管理\n>>>') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main()
二、python 函数递归
递归思考题
# 1*2*3*4*5*6 def fun(n): if n == 1: return 1 else: return fun(n - 1) * n ret = fun(3) print(ret)
三、生成器
1、生成器基础
# 找到比22大的数 li = [11, 22, 33, 44, 55] result = filter(lambda x: x > 22, li) print(result) # 输出具有生成指定条件数据的能力的一个对象 print(list(result)) # ************************************************************* def func(): yield 1 # 函数里面有 yield 那么这个函数就是生成器函数 yield 2 yield 3 ret = func() # 生成器函数中不自动返回结果,变成一个具有生成能力的东西 for i in ret: # 需要循环时才会输出结果 print(i)
2、生成器进阶
def func(): print(111) yield 1 print(222) yield 2 print(333) yield 3 ret = func() r1 = ret.__next__() # 进入函数找到yield,获取yield后面的数据,输出 print(r1) # 保存上一次执行的位置,下一次执行时,接着上一次的位置往下走 r2 = ret.__next__() # 进入函数找到yield,获取yield后面的数据,输出 print(r2) r3 = ret.__next__() # 进入函数找到yield,获取yield后面的数据,输出 print(r3)
3、基于生成器实现range功能
# ********** 生成器(具有生成能力)************ def myrange(arg): start = 0 while True: if start > arg: return yield start start += 1 # *********迭代器(具有取数据的能力)*********** ret = myrange(10) # 可以生成10个数据 """ r = ret.__next__() print(r) r = ret.__next__() print(r) r = ret.__next__() print(r) r = ret.__next__() print(r) """ for item in ret: # 迭代器不需要取写,内部生成。 print(item)
四、python 序列化
用于序列化的两个模块
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
1、python序列化__json
import json dic = {'k1': 'v1'} print(dic, type(dic)) # 将python基本数据类型转化成字符串形式 result = json.dumps(dic) print(result, type(result)) s1 = '{"k1": "v1"}' # 字符串 # 将已编码的json字符串解码为python对象(基本数据类型) dic1 = json.loads(s1) print(dic, type(dic1)) # py基本数据类型转换字符串 r = json.dumps([11, 22, 33]) li = '["cjj", "cjjjjj"]' # 一定要外面单引号,里面双引号 ret = json.loads(li) print(ret, type(ret)) # dump 比 dimps 多执行一步操作,把转换的字符串写入文件 l2 = [11, 22, 33, 44] json.dump(l2, open('db', 'w')) # 先序列化,然后写入文件 l2 = json.load(open('db', 'r')) # 先读文件,然后反序列化 print(type(li), li)
2、python序列化__pickel
# pickle 只能python用,不能夸语言 import pickle li = [11, 22, 33] r = pickle.dumps(li) print(r) r2 = pickle.loads(r) print(r2) pickle.dump(li, open('db2', 'wb')) # 以特殊的方式写进去 result = pickle.load(open('db2', 'rb')) print(result, type(type)) # json 更适合夸语言,字符串,基本数据类型 # pickle,更适合对python所有类型做操作,仅适用于python
3、基于天气API获取天气相关JSON信息
import requests import json response = requests.get('https://www.baidu.com/baidu?tn=monline_3_dg&ie=utf-8&wd=北京天气') requests.encoding = 'utf-8' print(response.text) dic = json.loads(response.text) print(dic, type(dic))
五、字符串格式化
1、字符串格式化__%
# 给占位符命名,通过字典的key 来传递参数 s = "大家好,我叫 %(name)s,今年 %(age)d岁了" %{'name': 'cjj', 'age': 22} print('s:', s) s1 = "大家好,我叫 %(name)-10s,今年 %(age)+10d岁了" %{'name': 'cjj', 'age': 22} print('s1:', s1) # -10 :右对齐,占10个单位;+10 :左对齐,占10个单位 s2 = '长度 %.2f' %1.236 # 保留2位小数,自动四舍五入 print('s2:', s2) s3 = 'cjj %c ------- %o ========== %x ======== %e ' %(65, 15, 15, 2000000000) print('s3:', s3) # %c:ASCLL码转换,%o:八进制,%十六进制,%e:科学计数法 s4 = 'cjj %' print('s1:', s1) # 当格式化时,字符串中出现占位符%s,需要用 %% 输出 % s5 = 'cjj %s %%' %('SB 100') print('s5:', s5)
2、字符串格式化__format
s = "-----{} ------ {} ----- {}".format(123, 'python', 15) print('s:', s) s1 = "-----{0} ------ {0} ----- {1}".format(123, 'python') print('s1:', s1) # 按照位置来赋值 s2 = "------ {name:s} ------ {age:d} ----- {name:s}".format(name='cjj', age=521) print('s2:', s2) # 根据名字来赋值 s3 = "-----{:6^20s}-------".format('cjj') print('s3:', s3) # :6^20s 占位符开始必须加冒号,6 :剩余部分用6填充,^ :居中,20 :长度,s: 字符串形式 print() # 填充的时候只能填充一个字符 s4 = "-----{:+d}-----{:#b}-----{:#o}-----{:#x}".format(123, 15, 15, 15) print('s4:', s4) # 数字前面加正号,转化成二进制,转化成八进制,转化成十六进制 s5 = "----- {:.2%}----".format(0.23451288) print("s5", s5) # 把数字转化成百分数, .2 表示保留小数后两位
六、时间处理
Directive | Meaning | Notes |
---|---|---|
%a |
Locale’s abbreviated weekday name. | |
%A |
Locale’s full weekday name. | |
%b |
Locale’s abbreviated month name. | |
%B |
Locale’s full month name. | |
%c |
Locale’s appropriate date and time representation. | |
%d |
Day of the month as a decimal number [01,31]. | |
%H |
Hour (24-hour clock) as a decimal number [00,23]. | |
%I |
Hour (12-hour clock) as a decimal number [01,12]. | |
%j |
Day of the year as a decimal number [001,366]. | |
%m |
Month as a decimal number [01,12]. | |
%M |
Minute as a decimal number [00,59]. | |
%p |
Locale’s equivalent of either AM or PM. | (1) |
%S |
Second as a decimal number [00,61]. | (2) |
%U |
Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. | (3) |
%w |
Weekday as a decimal number [0(Sunday),6]. | |
%W |
Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. | (3) |
%x |
Locale’s appropriate date representation. | |
%X |
Locale’s appropriate time representation. | |
%y |
Year without century as a decimal number [00,99]. | |
%Y |
Year with century as a decimal number. | |
%z |
Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59]. | |
%Z |
Time zone name (no characters if no time zone exists). | |
%% |
A literal '%' character. |
1、时间处理__time
import time print(time.time()) # 从1970年到现在计时(s),当时是Unix正式开始商用,时间戳 print(time.ctime()) # 默认返回当今时间:年月日时分秒 print(time.ctime(time.time()-86400)) # 一天86400秒,赋值一个时间戳,转化成字符串格式 time_obj = time.gmtime() print('\n', time_obj) # 格林威治时间 print(time_obj.tm_year, time_obj.tm_mon) print("{year}----{month}".format(year=time_obj.tm_year, month=time_obj.tm_mon)) print(time.localtime()) # 本地时间 print('\n', time.mktime(time_obj)) # 把时间对象(struct_time格式)转化成时间戳 time.sleep(4) # 延迟4秒输出,参数只能是秒 print("\n*************************************************************") print("\n", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) # 将struct_time格式转化成字符串格式 print(time.strptime("2017-12-25 11:52", "%Y-%m-%d %H:%M")) # 跟上面相反,将字符串格式转化为struct_time格式 tm = time.strptime("2017-12-25 11:52", "%Y-%m-%d %H:%M") print(time.mktime(tm)) # 将字符串格式先转化成struct_time格式,再转化成时间戳,
2、事件处理__datetime
import datetime print(datetime.date.today()) current_time = datetime.datetime.now() print('现在:\t', current_time) print('加10天 :', current_time + datetime.timedelta(days=10)) # 比现在加10天 print('减10天 :', current_time + datetime.timedelta(days=-10)) # 比现在减10天 print('加10小时:', current_time + datetime.timedelta(hours=10)) # 加10小时 print('更改时间:', current_time.replace(2014, 9, 18)) print('更改年 :', current_time.replace(2018)) print('更改年月:', current_time.replace(2018,6)) print('**********************************************************') print(datetime.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M"))
七、日志处理
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别,
下面我们看一下怎么用。
1、日志处理__logging
import logging # level 判断优先级,优先级等于或者大于INFO的日志会被记录 logging.basicConfig(filename='example.log', level=logging.DEBUG) logging.debug('This message should go to the log file') logging.info('So should this') logging.warning('And this, too')
其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
logging.basicConfig(filename='example.log', level=logging.DEBUG)
日志加上时间:
import logging logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') logging.warning('is when this event was logged.') #输出 12/12/2010 11:46:36 AM is when this event was logged.
日志格式:
%(name)s |
Logger的名字 |
%(levelno)s |
数字形式的日志级别 |
%(levelname)s |
文本形式的日志级别 |
%(pathname)s |
调用日志输出函数的模块的完整路径名,可能没有 |
%(filename)s |
调用日志输出函数的模块的文件名 |
%(module)s |
调用日志输出函数的模块名 |
%(funcName)s |
调用日志输出函数的函数名 |
%(lineno)d |
调用日志输出函数的语句所在的代码行 |
%(created)f |
当前时间,用UNIX标准的表示时间的浮 点数表示 |
%(relativeCreated)d |
输出日志信息时的,自Logger创建以 来的毫秒数 |
%(asctime)s |
字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 |
%(thread)d |
线程ID。可能没有 |
%(threadName)s |
线程名。可能没有 |
%(process)d |
进程ID。可能没有 |
%(message)s |
用户输出的消息 |
2、日志处理__进阶
把日志同时打印到屏幕和文件中:
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:
logger提供了应用程序可以直接使用的接口;
handler将(logger创建的)日志记录发送到合适的目的输出;
filter提供了细度设备来决定输出哪条日志记录;
formatter决定日志记录的最终输出格式。
logger
每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块可以这样:
LOG=logging.getLogger(”chat.kernel”)
Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别
handler
handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些
Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
1) logging.StreamHandler
使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr
2) logging.FileHandler
和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。
3) logging.handlers.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建
一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把
文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建
chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
4) logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就
自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨
import logging # create logger logger = logging.getLogger('TEST-LOG') # 先获取logger对象 logger.setLevel(logging.DEBUG) # 设置一个全局的日志级别 # create console handler and set level to debug 输出到屏幕和文件 ch = logging.StreamHandler() # 把日志打印到屏幕 ch.setLevel(logging.DEBUG) # 设置输出级别为DEBUG # create file handle and set level to debug 输出到文件 fh = logging.FileHandler("access.log") # 把日志向文件输出 fh.setLevel(logging.WARNING) # 设置级别WARNING # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter_file = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') # add formatter to ch and fh ch.setFormatter(formatter) # 设置屏幕的输出格式 fh.setFormatter(formatter_file) # 设置文件的输出格式 # add ch and fh to logger logger.addHandler(ch) # 把日志打印到指定的Handler里面 logger.addHandler(fh) # 'application' code logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
日志记录格式: