day11 装饰器---函数的使用方法
这个是一个难点,以后面试会经常出现的,要搞懂!
装饰器升级版,进阶内容1:
def outer(flag):
def wrapper(func):
def inner(*args,**kwargs):
if flag:
print('执行%s之前我加了一个功能'%func.__name__)
ret = func(*args,**kwargs) #qqxing
return ret
return inner
return wrapper
@outer(True) #qqxing = wrapper(qqxing)
def qqxing():
print('qqxing') @outer(False) #wahaha = wrapper(wahaha)
def wahaha():
print('wahaha') qqxing()
wahaha()
#
#
# # def outer(flag,flag2):
# def wrapper(func):
# def inner(*args,**kwargs):
# ret = func(*args,**kwargs)
# return ret
# return inner
# return wrapper
#
# @outer(True,False) #带参数的装饰器 ==》@wrapper ==》func = wrapper(func) ==> func == inner
# def func():pass
上面是原代码,比较简洁。如下我又重新复制了一遍,以便做更加详细的注释。
注释开始: def outer(flag):#这里需要加这个普通函数(没错这里的outer(flag)就是一个普普通通的函数而已,
#只不过它加在了装饰器的上面所以看起来好像很高级的样子)的目的是为了给装饰器里面的内容传一个参数,之所以传这个参数是为了调用里面加设的条件,如下所例:
def wrapper(func):
def inner(*args,**kwargs):
if flag:#接上面outer(flag)如果上面传入的flag是True那么就执行这里面的一步,否则就不执行。
#所以需要把上面的参数传入进来才可判断是否需要执行它
print('执行%s之前我加了一个功能'%func.__name__)#所以最后的最后,这里面需要加入什么样的判断条件或者什么功能都是可以随意改变的,
#完全取决于你想要什么样的需求
ret = func(*args,**kwargs) #qqxing
return ret
return inner
return wrapper#下面解释那么多都是为了给这里这一步做铺垫的,因为把True这个参数传进来了,函数调用后需要有返回值,
#这样后续才能有机会进入里面来接着执行没有执行完的内容。所以这里我们把wrapper给返回了。为了后续程序的执行做了铺垫。
@outer(True) #qqxing = wrapper(qqxing) 这里需要解释的是,既然上面加了一个函数outer(flag)所以这里需要调用它然后才可以进入该函数里面
#只有进入了函数里面才有机会执行函数内部的内容这里我们有专业术语叫做--函数体。所以把原本的@wrapper改成了最外层的函数的名字---@outer(True)
#同时传入了一个参数(True)作为判断条件给outer(flag)。然后,仔细看这一句话@ outer (True)这里面每一个都含藏玄机的,我来一一解释一下,
#‘@’这个符号是语法糖的符号,它后面接的是函数名这里是outer,仅仅这两个加在一起就足以调用一个函数了,但是函数名outer后面又加了一个括号(),
#形成了outer()这本身就足够条件调用一个函数了,然后括号里面还有一个True这就是一个函数在被调用的时候给它加入了一个参数,所以,你看,我们现在要如何判断呢
#是先判断@outer呢?还是先判断 outer(True)呢?现在来揭晓答案,是先判断outer(True),这一点是毫无疑问的,所以把outer(True)先调用,
#这里就是调用了上面最外层的函数outer(flag),同时把True这个参数传给了它。
#当把True传给了outer之后该函数的结果返回了,所返回的结果就是return wrapper,然后返回的结果被语法糖这里接收了。上面我们刚刚解释过了,
#@outer(True)这一句话的意思,根据逻辑可得,outer(True)的返回结果是wrapper,被语法糖接收,所以这里我们就把outer(True)替换成wrapper,
#那么得到的最终结果就是@wrapper。所以你看,我们绕了这么大一个圈最终回到了原点,是不是很奇妙啊,这就是语法的魅力,扯淡了。。。。
#当我们得到了最终的@wrapper这个结果的时候,那么我们在原本的简单的装饰器上面加的那一层outer函数就已经完成了它作为一普通函数的所有功能,
#然后它就消失在了代码的长河中,然后我们就可以把outer函数的内容给剥掉,最后得到的就是一个普通的装饰器的样子,然后你就只需要解决掉一个简单的装饰器即可了。
def qqxing():
print('qqxing') @outer(False) #wahaha = wrapper(wahaha)这里跟上面是一样的,就是多加一个outer判断而已,按照上面的程序再接着走一遍,把outer(False)
#先传给outer函数然后把结果返回给语法糖。
#需要提醒的是,最外层的这个outer可以自定义改成任何名字,参数也可以随意变换,但是要遵循一条基本原则,
#那就是你所传的参数和你所接收的参数需要数量上保持一致
def wahaha():
print('wahaha') qqxing()
wahaha()
#2000
#1000
#1000 # def outer(flag,flag2):
# def wrapper(func):
# def inner(*args,**kwargs):
# ret = func(*args,**kwargs)
# return ret
# return inner
# return wrapper
#
# @outer(True,False) #带参数的装饰器 ==》@wrapper ==》func = wrapper(func) ==> func == inner
# def func():pass
以上就是一个带参数的装饰器的所有内容了。
装饰器升级版,进阶内容2:
def wrapper2(func): #inner1
def inner2(*args,**kwargs):
print('in wrapper 2,before')
ret = func(*args,**kwargs) #inner1
print('in wrapper 2,after')
return ret
return inner2
def wrapper1(func): #qqxing
def inner1(*args,**kwargs):
print('in wrapper 1,before')
ret = func(*args,**kwargs) #qqxing
print('in wrapper 1,after')
return ret
return inner1 @wrapper1 #qqxing = inner1
@wrapper2 #qqxing = wrapper2(inner1) = inner2
def qqxing():
print('qqxing') qqxing() #inner2 #login 一个实现login的装饰器
#timmer 一个计算函数时间的装饰器 #先登录,再计算时间
同上,为了更方便理解,我把它复制过来以便于加注释:
执行步骤已经标注了。
1# def wrapper2(func): #qqxing #根据下面wrapper2上传的参数得到的结果,此时qqxing就等于func,
5# def inner2(*args,**kwargs): #这里是inner2函数,被调用了
10# print('in wrapper 2,before') #inner2被调用后打印这里
11# ret = func(*args,**kwargs) #qqxing #根据闭包函数的理论,这里的func是调用的上一层函数的参数,所以这里的func也等于qqxing
#按照从上到下原则,打印完之后到了这一步,这里又遇到了等式,然后先计算右边的,此时的func已经等于qqxing,所以这里调用了qqxing函数。
15# print('in wrapper 2,after') #上面走完了就到这里继续接着走下去,打印这一步。
16# return ret #然后顺着下来,返回结果
6# return inner2 #这里把wrapper2函数的结果返回了,
2# def wrapper1(func): #inner2 根据传参数的结果这里的func就等于inner2,
7# def inner1(*args,**kwargs): #这里的inner1被调用了
12# print('in wrapper 1,before') #打印结果
13# ret = func(*args,**kwargs) #inner2 同理根据闭包理论这里func调用的是上一层函数,所以这里的func也是inner2
#上一行被打印完了之后,到这里的时候是一个等式先计算等式右边的,右边的func已经等于了inner2了,所以在这一步调用了inner2函数
17# print('in wrapper 1,after') #按照顺序下来就轮到了这里,打印结果
18# return ret #这里就是最后一步
8# return inner1 #这里把wrapper1的函数结果返回了
先定义函数这一点没什么可解释的,这里是语法糖,在定义过函数后就走到了这里,然后走下面的语法糖
3# @wrapper1 #qqxing = inner1 #这里需要着重解释一下原因,然后是这里,@wrapper1----就解释为qqxing=wrapper1(inner2)之所以是这样的是因为,
#下面的wrapper2已经被调用执行过了,所以是qqxing=,然后=后面的内容应该是wrapper1(qqxing),但是下面的wrapper2执行之后把qqxing改变成了inner2了,所以括号里面应该是inner2
#所以最终得到的就是qqxing=wrapper1(inner2),把wrapper1调用了,然后把参数inner2传参数给wrapper1函数。上面调用完了该函数后得到的返回值是inner1.所以最终qqxing=inner1.
4# @wrapper2 #qqxing = wrapper2(inner1) = inner2 #这里得到的是跟之前学过的一样的结果,@wrapper2就解释为----qqxing=wrapper2(qqxing)
#然后开始调用wrapper函数顺便把qqxing作为参数传给wrapper2函数 。根据上面的执行结果得到的返回值inner2,所以上面的qqxing=wrapper2(qqxing)最终的返回结果就是inner2.
#所以最终的结果就是qqxing=inner2(qqxing的物理地址是inner2,表现形式是qqxing。这一句是老师的原话,如果不理解的话可以忽略不计的。)
def qqxing():
14# print('qqxing')#函数被调用然后打印出来 9# qqxing() #inner1 然后根据程序从上到下执行的结果,走到了这一步,这里的qqxing现在是inner1。所以qqxing()就等于inner1()。这里调用了inner1函数
根据上面的inner2里面的执行结果到了这一步,然后调用这里的qqxing函数 唯一难懂的一点就是语法糖的部分,一叠加之后,就会容易混淆。
思维导图:
import time 1
def time(func): 2
def inner(*args,**kwargs): 4
start=time.time() 8
re=func(*args,**kwargs) 9
print(time.time-start) 12
return re 13
return inner 5
@time 3
def func2(): 6
print('shero') 10
print('so what') 11 func2() 7 #程序内部的执行顺序
前情回顾:
# a=10
# b=20
# def tes5(a,b):
# print(a,b)
# c = tes5(b,a)
# print(c) # a=10
# b=20
# def tes5(a,b): #a = 20,b = 10
# a=3
# b=5
# print(a,b)
# c = tes5(20,10)
# print(c) # x = 1
# def f(x):
# print(x)
#
# ret = f(10)
# print(ret)
# print(x) def f1():
a = 1
def f2():
def f3():
print(a)
f3()
print('f3 : ',f3.__closure__)
f2()
print('f2 : ',f2.__closure__) f1() #动态参数*args,**kwargs :位置参数,*args,默认参数,**kwargs
#命名空间和作用域
#作用域链
#函数名是第一类对象的概念
闭包:(为了后面的装饰器做铺垫的)
# def f1(b):
# def f2():
# print(b)
# f2()
#
# f1()
#闭包的定义
#内部的函数引用了外部函数的变量 # def f1(b): #闭包的常用状态
# def f2():
# print(b)
# return f2
#
#
# # f2 = f1('bbb')
# ff = f1('bbb')
# ff() #==f1('bbb')() # def f1(): #从内部函数返回一个值到全局
# b = 10
# def f2():
# return b
# return f2()
#
# print(f1()) from urllib.request import urlopen
# ret = urlopen('http://www.cnblogs.com/Eva-J/articles/7194277.html').read()
# print(ret)
def get_url(url):
def read1():
ret = urlopen(url).read()
print(ret)
return read1 read_func = get_url('http://www.cnblogs.com/Eva-J/articles/7194277.html')
read_func()
read_func()
装饰器:
#讲故事
#带着你一步一步的剖析装饰器的成因
#关于时间:
import time #模块
# start_time = time.time()
# time.sleep(1)
# end_time = time.time()
# print('=====%s====='%(end_time-start_time))
def timmer(func):
def inner():
start_time = time.time()
time.sleep(0.1)
func()
end_time = time.time()
print('=====%s=====' % (end_time - start_time))
return inner def func():
print('公司好老板好同事好') # func = timmer(func)
# func() #装饰器的作用
# 在不改变函数的调用方式的情况下,给函数的前后添加新的功能 #从最简单的装饰器
def timmer(qqxing): #timmer是装饰器的名字,传入的参数就是被装饰的函数
def inner(): #在装饰器中需要定义一个内部函数
print('调用func之前')
qqxing() #被装饰的函数,并且要执行
print('调用func之后')
return inner #将内部函数的名字返回 @timmer #语法糖 func = timmer(func)
def func():
print('公司好老板好同事好') # func()
# 完整的装饰-万能的装饰
def timmer(qqxing): #timmer是装饰器的名字,传入的参数就是被装饰的函数
def inner(*args,**kwargs): #在装饰器中需要定义一个内部函数
print('调用func之前')
ret = qqxing(*args,**kwargs) #被装饰的函数,并且要执行
print('调用func之后')
return ret
return inner #将内部函数的名字返回 @timmer #语法糖 func = timmer(func)
def func(name):
print('%s的公司好老板好同事好'%(name))
return 1111111111 ret = func('俊杰')
print('result : %s'%ret) #装饰器的固定结构
def wrapper(func):
def inner(*args,**kwargs):
"""被装饰函数执行之前要添加的代码"""
ret = func(*args,**kwargs)
"""被装饰函数执行之后要添加的代码"""
return ret
return inner '''def timmer(qqxing): #第一步定义一个函数 #第六步接收了timmer传上来的参数func。此时qqxing=func #此时timmer=inner是被return返回的值
def inner(): #第四步,定义一个函数
qqxing() #第九步因为inner被调用所以执行inner里面的内容,因为第六步的时候qqxing已经被func赋值了,所以这一步就是func()等于调用了func()函数
return inner #第五步因为没有调用该函数,所以直接走下面不走函数里面。此时把inner返回给了return所在的函数timmer里,return在timmer里缩进的,所以return的位置决定了它是timmer的返回值
def func(): #第二步定义一个函数
print('hello') ##第十步因为func()函数被执行了,所以这一步就被打印出来了。
func=timmer(func) #第三步先计算=右边的 调用了timmer函数同时还传了一个参数 #第七步timmer的函数被return返回为inner,下面的timmer也是跟上面的timmer一样的所以要返回来,然后赋值给左边的func
func() #第八步因为没有调用函数,所以执行下一步func()因为上一步的func被赋值为inner,所以这一步的func调用就是等于inner()调用
'''
课堂小练习:
里面有一个是面试题,log。
#概念
#开放封闭原则
#开放
#对扩展是开放的 #封闭
#对修改是封闭的 #装饰器:开放封闭原则
'''
flag = False def login(func):
def inner(*args,**kwargs):
global flag
if flag == False:
username = input('用户名:')
password = input('密码:')
if username == 'alex' and password == 'somebody':
print('登录成功')
flag = True
if flag == True:
ret = func(*args,**kwargs)
return ret
return inner @login
def art():
print('欢迎来到文章页') @login
def dar():
print('欢迎来到日记页') #用装饰器实现,访问art或者dar函数,登陆一次之后,无需再次登录
art()
dar()
'''
def log(func):
def inner(*args,**kwargs):
print('你要调用%s函数了'%func.__name__)
ret = func(*args,**kwargs)
return ret
return inner @log
def f1():
print('f1')
@log
def f2():
print('f2') #日志 log 必须会!
f1()
f2() #背诵装饰器的固定结构
#复习函数以及装饰器的所有内容、继续完成思维导图和博客
#上面的登录、日志的题目 搞懂
#预习:
# 装饰器的进阶用法
# 迭代器和生成器
后期会补上思维导图,,,
day11 装饰器---函数的使用方法的更多相关文章
- Python使用property函数和使用@property装饰器定义属性访问方法的异同点分析
Python使用property函数和使用@property装饰器都能定义属性的get.set及delete的访问方法,他们的相同点主要如下三点: 1.定义这些方法后,代码中对相关属性的访问实际上都会 ...
- python基础篇_004_装饰器函数
python装饰器函数 1.装饰器函数引导 功能:计算函数执行时长 import time """ 方式一: 函数首位添加时间,差值就是函数执行时间 缺点:每个函数都要加 ...
- python基础(8)-装饰器函数&进阶
从小例子进入装饰器 统计一个函数执行耗时 原始版本 import time # time模块有提供时间相关函数 def do_something(): print("do_something ...
- Python学习日记(九) 装饰器函数
1.import time a.time.time() 获取到当前的时间,返回值为浮点型 import time print(time.time()) #1565422783.6497557 b.ti ...
- day20-Python运维开发基础(装饰器 / 类中的方法 / 类的方法变属性)
1. 装饰器 / 类中的方法 / 类的方法变属性 # ### 装饰器 """ 定义:装饰器用于拓展原来函数功能的一种语法,返回新函数替换旧函数 优点:在不更改原函数代码的 ...
- classmethod、staticclassmethod内置装饰器函数
# method 英文是方法的意思 # classmethod 类方法 # 当一个类中的方法中只涉及操作类的静态属性时,此时在逻辑上,我们想要直接通过类名就可以调用这个方法去修改类的静态属性,此时可以 ...
- property内置装饰器函数和@name.setter、@name.deleter
# property # 内置装饰器函数 只在面向对象中使用 # 装饰后效果:将类的方法伪装成属性 # 被property装饰后的方法,不能带除了self外的任何参数 from math import ...
- 第7.26节 Python中的@property装饰器定义属性访问方法getter、setter、deleter 详解
第7.26节 Python中的@property装饰器定义属性访问方法getter.setter.deleter 详解 一. 引言 Python中的装饰器在前面接触过,老猿还没有深入展开介绍装饰 ...
- day11.装饰器初识
1.开放封闭原则 原则: 开放封闭原则,对扩展是开放的,对修改是封闭的. 封版概念:当写好一个功能以后,就不可以再修改此函数,避免下面一系列的调用产生错误. 因此产生了装饰器 2.装饰器形成过程 我们 ...
随机推荐
- Django 笔记(三)模版路径 ~ 静态引用
1.模版路径: 在 settings,py 里的 TEMPLATES = [] 内添加一句代码拼接路径 'DIRS': [os.path.join(BASE_DIR, 'templates')] 有两 ...
- 获得小程序码getWXACodeUnlimit
报错47001 data format error 出现这个错误必须是Body里面的raw才可以,而且access_token参数必须写在地址后面,不能写在raw里面,不然也出错. /** * 生命周 ...
- JAVA实现网络文件下载
HttpURLConnection conn = null; OutputStream outputStream = null; InputStream inputStream = null; try ...
- hdu2602 Bone Collector 01背包
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like ...
- CSS弹性(flexible)盒子
弹性盒子 弹性盒子由弹性容器(Flex container)和弹性子元素(Flex item)组成 弹性容器通过display:flex | inline-flex将其定义为弹性容器 ...
- swift 实践- 02 -- 自定义cell 的简单使用
import UIKit class MyTableViewCell: UITableViewCell { var imageV: UIImageView? var titleLabel: UILab ...
- swift 学习- 22 -- 嵌套类型
// 枚举 常备用于为特定的类 或 结构体实现某些功能, 类似的, 枚举可以方便的定义工具类 或 结构体, 从而为某个复杂的类型所使用, 为了实现这种功能, Swift 允许你定义 嵌套类型, 可以在 ...
- css固定表头,表单内容可以滑动
<html><head> <meta charset="utf-8"> <title>Table</title&g ...
- SpringCloud路由(网关)
springcloud网关接口就类似于转发 搭建路由网关项目(ZuulDemo) 1.创建pom.xml <project xmlns="http://maven.apache.org ...
- 【linux】复制文件夹内容到另一个文件夹
我一直觉得cp是个非常简单的指令.结果居然遇到坑了.记录一下. 文件夹1:test1/ 文件夹2:test2/ 目标:将test1/中的所有文件和目录拷贝到test2/中 正确指令: cp -rf t ...