文章目录
博客cPen_web
知识点1 Python闭包
示例1:闭包
#生命周期
# a=1 定义时开始,执行完结束。生命周期定义了程序的开始与结束
#1、闭包
def func1():
a = 10
print(f" test {a}")
func1() # 注:函数调用完 a就释放了,生命周期是函数调用的时间
# print(a) # 注:打印不出来
#变量解析原则:LEGB原则
def outer(x):
a = 278
def inner(): # 注:inner先在自己里面找x和a
print(x+a) # 注:再从上一层函数找
return inner # 得到的是inner这个函数对象
#返回这个函数对象
# return inner() # 返回的是函数的返回结果 默认返回None
# inner() 执行的返回结果是None,return None [有括号表示执行了的函数]
result = outer(10)
result() # 注:因为返回的是函数,所以result()是执行函数
#结果为 288 # 注:内部定义的变量 没有随着outer函数 结束掉,会被inner函数所引用,这就是闭包
#注:内部定义的变量,因为被内部函数所引用,所以不会被销毁
示例2:闭包满足的条件
#闭包满足的条件
#1、必须要有内嵌函数(外函数和内函数)
#2、内函数必须调用外函数的变量
#2、外函数必须返回内函数
示例3:形成闭包之后,内函数会获得一个非空的__closure__属性
#形成闭包之后,内函数会获得一个非空的__closure__属性
def func1():
a = 10
print(f" test {a}")
func1() # 注:函数调用完 a就释放了,生命周期是函数调用的时间
def outer(x):
a = 278
def inner(): # 注:inner先在自己里面找x和a
print(x+a) # 注:再从上一层函数找
return inner # 得到的是inner这个函数对象
#--------------------------------------------------
result = outer(10)
print(dir(result))
#结果为 […… '__class__', '__closure__',……]
print(result.__closure__) # 注:这个属性里保存了外部函数的变量。
#结果为 (<cell at 0x000001CD121065B0: int object at 0x000001CD12117910>, <cell at 0x000001CD12106B80: int object at 0x00007FFF2ABC07C0>)
示例4:记住函数的执行时间和加入时间
#注:闭包的坑 注意闭包函数返回的执行时间
#记住函数的执行时间和加入时间
def outer():
fs = [] # 注:定义列表
for i in range(5):
def inner(): # def是创建函数变量
return i*i # 注:形成了闭包:i是outer的变量,被inner引用,返回的是inner的变量
fs.append(inner) # 存放5次,inner函数没有执行。只是把这个变量加入了5次
# 注:这个inner是函数对象,并不是执行后的结果
return fs
fs1, fs2, fs3, fs4 ,fs5 = outer() # 列表返回 赋值.可以这样写
#注:执行的时候 执行的是i=4 inner里面保存的是4 4*4=16
print(fs1()) # 注:fs1实际是函数inner fs1()实际是执行inner函数
print(fs2())
print(fs3())
print(fs4())
print(fs5())
#结果为 16
# 16
# 16
# 16
# 16
#注:调用的时候,才开始执行,开始执行的时候,i已经变成4了
#注:print(fs5()) 打印 实际是打印函数执行的输出
示例5:列表里的值赋值
#列表里的值都可以这样复制,元组也是
a, b = [1, 2]
print(a, b)
#结果为 1 2
知识点2 什么是装饰器
#装饰器
#装饰器的本质就是闭包函数,在不修改某一功能函数或类的情况下,为它们添加一些额外的功能,通常就使用装饰器
#把函数作为参数,形成闭包,就是装饰器
示例1:装饰器 函数执行时间
#装饰器
import time
import datetime
def func1(a):
b = a + 2
print(b)
time.sleep(1) # 注:睡1s
def func2():
print("this is func2")
time.sleep(1)
#写法1-------------------------------------------------------------------------------------
start = time.time() # 注:time.time() 获取时间戳
func1(10) # 注:时间戳:从1970年到现在经历了多少秒
end = time.time()
print(end - start)
#结果为 1.0001559257507324
#注:func1函数前/后 获取时间,相减获取运行时间
#注:Python里处理时间的模块:time、datetime
#写法2-------------------------------------------------------------------------------------
#注:datetime 模块
t1 = datetime.datetime.now()
func1(10)
t2 = datetime.datetime.now()
print(t2 - t1)
#结果为 0:00:01.000106
#写法3-------------------------------------------------------------------------------------
#闭包
def runtime(func): # 注:传递函数参数
# print(id(func)) # 注:参数对象的id是定义好的函数的id 不是后面的inner函数
def inner(*args): # 注:接收可变长位置参数,这是func函数的参数
start = time.time()
func(*args) # 注:通过inner接收函数的参数,解包
end = time.time()
print(f"函数执行花费时间:{end - start}")
return inner
#注:runtime(func)函数 传参是函数,里面inner(*args)里,传参和func函数传的参数一样,里面运行了func函数 (func(*args))
result = runtime(func1)
result(10) # 注:本质inner函数 传递参数进去
#结果为 12
# 函数执行花费时间:1.0000476837158203
result2 = runtime(func2)
result2()
#结果为 this is func2
# 函数执行花费时间:1.0005884170532227
#把函数作为参数去传递,形成1个闭包,就是装饰器
#装饰器只能装饰函数或者类
#为函数添加额外功能 就是装饰器
#装饰器的本质就是闭包,装饰器:把函数作为1个参数传递给闭包函数
#装饰器的本质是闭包
@runtime # 注:--> 其实是 执行了runtime(func3) @ 是语法糖
def func3():
print("i am func3")
time.sleep(2)
func3 = runtime(func3) # 注:相当于执行的这个 func3相当于重新形成的inner函数
func3()
#结果为 i am func3
# 函数执行花费时间:2.0000953674316406
#结果为 2642713760384
#结果为 2642713545168
#注:func3() 相当于新的函数 返回的函数inner() 与原来的func3()函数不一样
示例2:增加log日志的装饰器
#增加log日志的装饰器
#执行某个函数的时候,输出"执行了xxx函数" #注:相当于为函数增加额外功能
import time
def log_func(func): # 注:接收函数作为参数传入
#注:外函数
print("this is log_func")
def _log_func(*args, **kwargs): # 因为可变长,所以能接收0-n个
#注:内函数引入了外函数的变量
print(f"执行了{func.__name__}") # 注:__name__返回类名
func(*args, **kwargs) # 注:执行函数
return _log_func # 注:不要加括号
#注:返回了内函数
@log_func # 相当于执行了 func1 = log_func(func1)
def func1():
print("this is func1")
time.sleep(2)
func1()
#结果为 this is log_func
# 执行了func1
# this is func1
练习3 权限认证装饰器
用装饰器实现权限控制
定义一个全局变量:username
定义add函数,实现两个数相加
实现login_required装饰器,如果username值为root,提示"欢迎" 并计算结果,否则"没有权限"
示例
def login_required(func):
def judge(*args):
if username in users.keys():
print("欢迎")
func(*args)
else:
print("没有权限")
return judge
@login_required
def add(a,b):
result = a+b
print(result)
add(1,2)
知识点4 logging模块 记录日志
###logging 模块
#日志记录
#日志 记录所发生的事情
#作用 可以快速定位故障,做一些用户行为统计分析
#logging模块 记录日志
###日志等级
#debug # 记录最详细的日志信息 用于调试代码 (可以记录到某个文件里面)
#info # 详细程度仅次于debug,记录关键节点信息
#warning # 当某些不期望的事情发生了,但是程序正常运行
#error # 一个严重的问题,引起某些功能不能正常运行
#critical # 发生严重错误,程序不能正常运行时记录的信息
#默认日志等级是 warning
#大家约定俗称去使用等级
#-------------------------------------------------------------------------------------------
###logging 模块
#日志记录
#日志 记录所发生的事情
#作用 可以快速定位故障,做一些用户行为统计分析
#logging模块 记录日志
###日志等级
#debug # 记录最详细的日志信息 用于调试代码 (可以记录到某个文件里面)
#info # 详细程度仅次于debug,记录关键节点信息
#warning # 当某些不期望的事情发生了,但是程序正常运行
#error # 一个严重的问题,引起某些功能不能正常运行
#critical # 发生严重错误,程序不能正常运行时记录的信息
#默认日志等级是 warning
#logging.basicConfig 进行日志相关设置
#示例1
#-------------------------------------------------------------------------------------------
#第一种
#用于配置简单日志
#示例
import logging
logging.basicConfig(filename = "t.log", level=logging.DEBUG) #注:修改为输出到屏幕、debug以上输出
logging.debug("this is debug log")
#没有输出,默认情况下 只会输出warning以上等级
#debug、info输出太多了,不显示 不关注
logging.error("this is warning log")
#结果为 ERROR:root:this is warning log
logging.error("this is error log")
#结果为 ERROR:root:this is error log # 注:打印到屏幕了
# logging.basicConfig(filename = "t.log", level=logging.DEBUG) #修改为输出到屏幕、debug以上输出
#结果为 (输出到屏幕)
# DEBUG:root:this is debug log
# ERROR:root:this is warning log
# ERROR:root:this is error log
#示例2
#-------------------------------------------------------------------------------------------
#第二种
#logging日志系统的组件
#日志器 Logger 用来记录日志的
#处理器 Handler
#过滤器 Filter
#格式器 Formater
import logging
#获取logger对象
logger = logging.getLogger() # 注:返回1个日志处理对象
#创建1个handle对象,输出到文件
fh = logging.FileHandler("test.log") # 输出到test.log文件
#创建1个handler对象,输出到屏幕
ch = logging.StreamHandler()
#创建1个formatter对象,指定日志格式
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s: %(message)s")
#绑定format到handler上
fh.setFormatter(format) #注:只绑定到 输出到文件里的fh
#绑定handler到logger对象上
logger.addHandler(fh)
logger.addHandler(ch)
logger.setLevel(logging.DEBUG) # 设置等级 Debug以上都输出
logger.info("this is info log")
logger.error("this is error log")
#结果为(屏幕)
# this is info log
# this is error log
#结果为(test.log文件里)
# 2020-11-27 17:13:45,387 - root - INFO: this is info log
# 2020-11-27 17:13:45,387 - root - ERROR: this is error log
练习5 应用多个装饰器
#1、编写2个装饰器
#2、编写函数func1 实现两数之和相加,接收2个参数传入
#3、装饰器1:输出函数运行时间
#4、装饰器2:记录执行的函数,以及传入的参数,记录到日志文件
#5、装饰器执行的时候,有日志记录
#6、看一下装饰器执行顺序
示例
#1、编写2个装饰器
#2、编写函数func1 实现两数之和相加,接收2个参数传入
#3、装饰器1:输出函数运行时间
#4、装饰器2:记录执行的函数,以及传入的参数,记录到日志文件
#5、装饰器执行的时候,有日志记录
#6、看一下装饰器执行顺序
import time,logging
logger = logging.getLogger()
fh = logging.FileHandler('my.log') # 注:encoding=指定编码
format = logging.Formatter('%(asctime)s - %(message)s')
fh.setFormatter(format)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)
def runtime(func):
logger.info("执行花费事件装饰器")
def _runtime(*args):
before_time = time.time()
func(*args) # 注:执行的是_log
after_time = time.time()
result = after_time - before_time
logger.info('执行时间:' + str(result))
return _runtime
def log(func):
logger.info("执行日志记录装饰器")
def _log(*args):
func_name=func.__name__
func(*args)
logger.info(f'this is info log:func_name:{func_name},parameter:{args}')
return _log
@runtime # func1 = runtime(func1) 此func1是log装饰器返回的内函数
@log # func1 = log(func1)
def func1(a,b):
print(f'{a}+{b}={a+b}')
func1(1,2)
#结果为
# 2020-11-28 10:25:19,578 - 执行日志记录装饰器
# 2020-11-28 10:25:19,579 - 执行花费事件装饰器
# 2020-11-28 10:25:19,579 - this is info log:func_name:func1,parameter:(1, 2)
# 2020-11-28 10:25:19,579 - 执行时间:0.0
#第1步:@log -->执行的是log(func1) 返回的函数 交给runtime装饰
#第2步:@runtime -->runtime(log(func1))
#执行时:先log(func1) --> 返回_log --> runtime(_log) --> _runtime
#执行func(1,2) --> _runtime(1,2)
#封装时:由下而上
#执行时:由上而下
知识点6 编写和使用装饰器
示例1:保留元数据
#1、编写2个装饰器
#2、编写函数func1 实现两数之和相加,接收2个参数传入
#3、装饰器1:输出函数运行时间
#4、装饰器2:记录执行的函数,以及传入的参数,记录到日志文件
#5、装饰器执行的时候,有日志记录
#6、看一下装饰器执行顺序
import time,logging
import functools
logger = logging.getLogger()
fh = logging.FileHandler('my.log')
# fh = logging.FileHandler(path, encoding='utf-8') # 这是指定文件编码win默认gbk
format = logging.Formatter('%(asctime)s - %(message)s')
fh.setFormatter(format)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)
def runtime(func):
logger.info("执行花费事件装饰器")
@functools.wraps(func) # 把本身func函数的元数据 复制给runtime
# 保留元数据 元数据里面保存了它的函数名 文档字符串 注解
def _runtime(*args):
before_time = time.time()
func(*args) # 注:执行的是_log
after_time = time.time()
result = after_time - before_time
logger.info('执行时间:' + str(result))
return _runtime
def log(func): # log接收的_runtime已经变成func (即func1)
logger.info("执行日志记录装饰器")
def _log(*args):
func_name=func.__name__
func(*args)
time.sleep(2)
logger.info(f'this is info log:func_name:{func_name},parameter:{args}')
return _log
@log # func1 = log(func1) 此func1是runtime装饰器返回的内函数
@runtime # func1 = runtime(func1)
def func1(a,b):
print(f'{a}+{b}={a+b}')
time.sleep(1)
func1(1,2)
#结果为 my.log文件里
# 2020-11-28 10:44:06,442 - 执行花费事件装饰器
# 2020-11-28 10:44:06,442 - 执行日志记录装饰器
# 2020-11-28 10:44:07,442 - 执行时间:1.0005865097045898
# 2020-11-28 10:44:09,443 - this is info log:func_name:func1,parameter:(1, 2)
#封装时:由下而上
#执行时:由上而下
#函数带参数
#建议闭包函数使用*args,**kwargs传递参数
#无论装饰的函数,需要什么参数,装饰器都可以去装饰,比较通用
示例2:函数带参数
import functools
def runtime(func):
print("执行花费事件装饰器")
@functools.wraps(func) # 把本身func函数的元数据 复制给runtime
# 保留元数据 元数据里面保存了它的函数名 文档字符串 注解
def _runtime(*args, **kwargs):
before_time = time.time()
func(*args, **kwargs) # 注:执行的是_log
after_time = time.time()
result = after_time - before_time
print('执行时间:' + str(result))
return _runtime
@runtime
def func1(num1):
print("i am func1")
@runtime
def func2():
print("i am func2")
@runtime
def func3(num1,num2,default = 1):
print("i am func3")
func1(10)
func2()
func3(10,num2=2,default=10)
#结果为
# 执行花费事件装饰器
# 执行花费事件装饰器
# 执行花费事件装饰器
# i am func1
# 执行时间:0.0
# i am func2
# 执行时间:0.0
# i am func3
# 执行时间:0.0
#装饰器带参数
#@dec(args)
# def func():
# pass
#自身不传入参数的装饰器 (采用2层函数定义装饰器)
#自身传入参数的装饰器 (采用3层函数定义装饰)
示例3:装饰器带参数
import functools,time
def deco(args):
print("带参数的装饰器:", args)
def runtime(func): # 中间函数
print("执行花费事件装饰器")
@functools.wraps(func) # 保留原数据
def _runtime(*args, **kwargs):
before_time = time.time()
# result1 = func(*args, **kwargs)
func(*args, **kwargs)
after_time = time.time()
result = after_time - before_time
print('执行时间:' + str(result))
# return result1
return _runtime
return runtime # 注意把中间函数返回
@deco
def func1(a,b):
print(" i am func1")
return a+b
#结果为 带参数的装饰器: <function func1 at 0x000001F9C8DD8D30> # 传入的是func1
@deco(1)
######runtime = deco(1)
######func1 = runtime(func1)
def func1(a,b):
print(" i am func1")
return a+b
#结果为
# 带参数的装饰器: 1
# 执行花费事件装饰器
func1(1,2)
#结果为
# 带参数的装饰器: 1
# 执行花费事件装饰器
# i am func1
# 执行时间:0.0
result = func1(1,2)
print(result)
#结果为 None # 因为 return _runtime 没有返回值
#-----------------------------------------------
#如何获取返回值
def deco(args):
print("带参数的装饰器:", args)
def runtime(func):
print("执行花费事件装饰器")
@functools.wraps(func)
def _runtime(*args, **kwargs):
before_time = time.time()
result1 = func(*args, **kwargs) # 修改这里
after_time = time.time()
result = after_time - before_time
print('执行时间:' + str(result))
return result1 # 修改这里
return _runtime
return runtime
result = func1(1,2)
print(result)
#结果为
# ……
# 3
示例4:带参数的装饰器,如果username = root可以执行func1,不等于root 提示没有权限
#带参数的装饰器,如果username = root可以执行func1,不等于root 提示没有权限
#自身不传入参数的装饰器 (采用2层函数定义装饰器)
#自身传入参数的装饰器 (采用3层函数定义装饰)
import functools,time
c = input("请输入你的username:")
def deco(username):
print("带参数的装饰器",username)
def runtime(func):
print("执行花费时间装饰器")
@functools.wraps(func)
def _runtime(*args,**kwargs):
if username == "root":
before_time = time.time()
func(*args,**kwargs)
after_time = time.time()
result = after_time - before_time
print("执行时间"+str(result))
else:
print("你没有权限这么做")
return _runtime
return runtime
@deco(username = c)
def func1(a,b):
print("i am func1")
return a+b
result = func1(1,2)
print(result)
#结果为
# 请输入你的username:root
# 带参数的装饰器 root
# 执行花费时间装饰器
# i am func1
# 执行时间0.0
# None
#从最外层含糊开始执行,返回最里面的内函数为止,就不去执行了
#调用函数 实际是执行内函数
示例5:让装饰器更加通用一点
#让装饰器更加通用一点
#1、@deco 2、@deco() 3、@deco(username = c)
#通过默认参数做一些通用化的设置
import functools,time
c = input("请输入你的username:")
def deco(decorated_ = None,username=None): # 注:使用默认参数,实现@deco()/@deco(username = c)2种装饰方法
print("带参数的装饰器",username)
def runtime(func):
print("执行花费时间装饰器")
@functools.wraps(func)
def _runtime(*args,**kwargs):
if username == "root":
before_time = time.time()
func(*args,**kwargs)
after_time = time.time()
result = after_time - before_time
print("执行时间"+str(result))
else:
print("你没有权限这么做")
return _runtime
if decorated_:
return runtime(decorated_)
return runtime
# @deco(username = c) #decorated_ = None,username = c
# @deco
# _runtime = deco(func1) 相当于把func1传递给deco 返回_runtime
@deco() #decorated_ = None,username = None
#runtime = deco() 返回runtime
#_runtime = runtime(func1) 返回_runtime
@deco # _runtime=deco(func1) #decorated_ = func1,username = None
#这种写法,必须用关键字参数
#@deco("root") # 此类传参不支持
def func1(a,b):
print("i am func1")
return a+b
result = func1(1,2)
print(result)
练习7 用类实现装饰器
#注:语法糖接收callable对象,一般是函数或者类
示例:基于类实现装饰器
#基于类实现装饰器
#装饰器需要一个callable对象,并且返回1个callable对象,用类去实现也是可以的
#让类的构造函数接受1个函数,使用__call__方法处理并且返回1个函数
#不带参数的装饰器
class A:
def __init__(self,func):# 构造函数接受函数
self.func = func # func实例化的 时候传
def __call__(self, *args, **kwargs): # 把要做的事情放到__call__里面
print("this is A.__CALL__")
return self.func(*args, **kwargs)
@A # 类生成了实例化对象,语法糖帮做了
def func1(): # 语法糖调用call方法,返回1个函数,执行里面的语句
print("i am func1")
func1()
#结果为
# this is A.__CALL__
# i am func1
#注:类必须实现init和call方法,init必须接收1个函数,call接收参数
#带参数的装饰器
class A:
def __init__(self,name):
self.name = name
def __call__(self, func):
# print("this is A.__CALL__")
# return self.func(*args, **kwargs)
def deco(*args, **kwargs):
print("i am deco")
print(self.name)
func(*args, **kwargs)
return deco
@A("wy")
def func1():
print("i am func1")
func1()
#结果为
# i am deco
# wy
# i am func1
#函数执行时间
import time
class A:
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
start = time.time()
self.func(*args, **kwargs)
end = time.time()
print(f"运行时间:{end - start}")
@A
def func1():
print("i am func1")
time.sleep(1)
func1()
#结果为 运行时间:1.0006420612335205
练习8 按照对象创建时间排序
示例
#示例:按照对象创建时间排序
import time
class Sortable():
def __init__(self,name):
self.name = name
self.create_time = time.time() #获取当前时间
def __repr__(self): # 官方说明,这行是为了输出
return self.name+"xxx"
def __lt__(self, other): # 小于
if self.create_time < other.create_time:
return True # 小于返回Free
return False # 大于返回False
first = Sortable("first")
time.sleep(0.2)
second = Sortable("second")
time.sleep(0.2)
third = Sortable("third")
result = [third, first, second] # 对列表进行排序
print(sorted(result)) # sorted函数对列表进行排序
#结果为 [firstxxx, secondxxx, thirdxxx]
#注:print打印对象 调用repr方法
#示例:没有小于号的行为
class Sortable(): # 注:没有小于号的行为
def __init__(self,name):
self.name = name
def __repr__(self):
return self.name
first = Sortable("first")
time.sleep(0.2)
second = Sortable("second")
time.sleep(0.2)
third = Sortable("third")
result = [third, first, second]
print(sorted(result))
#报错 TypeError: '<' not supported between instances of 'Sortable' and 'Sortable'
#示例:给类添加装饰器
#给类添加装饰器
import functools
import time
def sorted_by_create_time(cls): # 注:接收类
original_init = cls.__init__ # 注:保存原来的init(保留原数据)
#给init函数 添加self.create_time属性
@functools.wraps(original_init) # 注:保留源数据
def new_init(self, *args, **kwargs):
original_init(self, *args, **kwargs) # 注:保留原来属性
self.create_time = time.time() # 给类添加create_time属性
cls.__init__ = new_init # 注:修改cls的init参数(添加了create_time属性)
#定义小于号的行为
def lt(self, other):
if self.create_time < other.create_time:
return True
return False
cls.__lt__ = lt # 注:修改cls的lt参数
return cls #把修改好的类返回
@sorted_by_create_time
class Sortable():
def __init__(self,name):
self.name = name
def __repr__(self):
return self.name
first = Sortable("first")
time.sleep(0.2)
second = Sortable("second")
time.sleep(0.2)
third = Sortable("third")
result = [third, first, second]
print(sorted(result))
#结果为 [first, second, third]