day04-装饰器

一、装饰器定义

1)装饰器:本质是函数。

2)功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的。

二、原则

1)不能修改被装饰函数的源代码

2)不能修改被装饰函数的调用方式

如果你写的这个程序在生产环境下已经运行了,如果修改别人的源代码或者修改别人的调用方式,那么出了问题,后果可想而知

三、实现装饰器知识储备

1)函数即"变量"

2)高阶函数

3)嵌套函数

最终: 高阶函数+嵌套函数 => 装饰器

 

3.1 函数即变量

1)python的内存机制

#变量

x = 1

#函数

def test():

pass

以上一个变量一个函数在内存中的表现形式如下图:

 

在python解释器中,有一个概念叫做引用基数,比方说,x=1,它会先在内存当中把1这个值实实在在的存放下来,这个x其实就是1的门牌号,也是对1的一次引用。python什么时候把这个1这个屋子清空呐?它会等到1所对应的门牌号都没有了,就会把1这里面的东西给清掉,这个也是python的内存回收机制,就是靠这种方式回收的。

2)del清理

那我们用什么清理呐?用del去清理门牌号,就是对1的值引用的变量,del  x就表示清理掉1对应的x的门派号。如果x没有被del,则x永远不还被删除,除非程序结束了,不然永远不会被删除。del删除的不是1,只是把门牌号x删除了,只是定期刷新时,发现1没有被其他门牌号引用了,才会被清掉。

3)函数在内存的表现形式

①bar函数在foo函数之后定义

#bar函数在foo函数之后定义
def foo():print("in the foo")bar()def bar():print("in the bar")foo()

#输出

in the foo

in the bar

bar函数是在foo函数之前定义

# bar函数是在foo函数之前定义
def bar():print("in the bar")def foo():print("in the foo")bar()foo()

#输出

in the foo

in the bar

显然,两种写法效果是一样的,那我们来看看第三种情况。

③bar函数在foo函数调用之后声明

# bar函数在foo函数调用之后声明
def foo():print("in the foo")bar()foo()def bar():print("in the bar")

#输出

Traceback (most recent call last):

in the foo

  File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 31, in <module>

    foo()

  File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 29, in foo

    bar()

NameError: name 'bar' is not defined  #bar函数没有定义

 

3.2 高阶函数

实现高阶函数有两个条件:

1)把一个函数名当做实参传给另外一个函数

2)返回值中包含函数名

1、把一个函数名当做实参传给另外一个函数

作用:在不修改被装饰函数源代码的情况下为其添加功能

import  time
def bar():time.sleep(3)print("in the bar")def test1(func):print(func)         #相当于print(bar) 函数的内存地址start_time = time.time()func()              #相当于bar() 进入函数内部执行stop_time = time.time()print("the func run the is %s"%(stop_time-start_time))
#没有修改bar的代码
test1(bar)  #把bar函数名当做实参传到test1中

  

#输出

<function bar at 0x0000000000A7D378>  #bar函数的内存地址

in the bar                               #函数值

the func run the is 2.9912972450256348

2、返回值中包括函数名

作用:不修改函数调用方式

import  timedef bar():time.sleep(3)print("in the bar")def test2(func):print(func)return func   #返回函数的内存地址#调用test2函数
bar = test2(bar)   #重新给bar赋值,打印内存地址(内存地址加上小括号就能打印函数值)
bar()  #bar函数调用方式不变,实现装饰器的功能

 

相当于@bar

#输出

<function bar at 0x0000000000B6D378>  #打印bar函数的内存地址

in the bar

3.3 嵌套函数

1、定义

在一个函数的函数体内,用def 去声明一个函数,而不是去调用其他函数,称为嵌套函数。

嵌套函数例子:

def foo():print("in the foo")def bar():  #在foo函数体内,用def声明一个函数print("in the bar")bar()
#调用foo函数
foo()

 

#输出

in the foo

in the bar

下面这种情况是不是嵌套函数? 

def bar():print("in the bar")def foo():print("in the foo")bar()  #调用bar函数foo()

很显然不是,因为只是调用了bar函数,没有用def去声明一个函数。

  

局部作用域和全局作用域的访问顺序

#局部作用域和全局作用域的访问顺序
x=0
def grandpa():x=1def dad():x=2def son():x=3print(x)son()dad()
#调用grandpa
grandpa()

很显然最后输出的是3,这个说明作用域:只能是从里往外找,一层一层的的找。

四、装饰器实现

4.1 定义

装饰器实现的条件:高阶函数+嵌套函数 =》装饰器

import time#定义内置函数
def timmer(func):  #timmer(test1) func=test1def deco():start_time = time.time()func()   #run test1()stop_time = time.time()print("the func run time is %s"%(stop_time-start_time))return deco           #返回deco的内存地址#装饰test1函数
@timmer  
# 相当于test1 = timmer(test1)      test1(),调用deco的内存值。同时也有定义一个test1的变量
def test1():time.sleep(3)print("in the test1")#直接执行test1函数
test1()

#输出

in the test1

the func run time is 3.0002999305725098

执行步骤:

  1. 执行timmer函数,timmer(test1) 返回值赋值给test1变量,即test1=timmer(test1)
  2. 此时的test1的值是执行timmer函数返回值deco,即test1=deco
  3. 所以执行test1,其实就是执行的是deco函数,test1()其实就是执行deco函数。

4.2 执行函数带参数

import timedef timmer(func):  #timmer(test2) func=test2def deco():start_time = time.time()func()   #run test2()stop_time = time.time()print("the func run time is %s"%(stop_time-start_time))return deco@timmer
def test2(name,age):print("name:%s,age:%s"%(name,age))test2()

  

#输出

Traceback (most recent call last):

  File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 23, in <module>

    test2()

  File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 8, in deco

    func()   #run test1()

TypeError: test2() missing 2 required positional arguments: 'name' and 'age' #缺少传入name和age参数

 

很显然是错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?

传入确定参数:

import timedef timmer(func):  #timmer(test1) func=test1def deco(name,age):start_time = time.time()func(name,age)   #run test2()stop_time = time.time()print("the func run time is %s"%(stop_time-start_time))return deco@timmer
def test2(name,age):print("name:%s,age:%s"%(name,age))test2('zhou',22)

  

不能确定传入几个参数,所以我们用非固定参数传参。代码如下:

import timedef timmer(func):  #timmer(test1) func=test1def deco(*args,**kwargs):  #传入非固定参数start_time = time.time()func(*args,**kwargs)   #传入非固定参数stop_time = time.time()print("the func run time is %s"%(stop_time-start_time))return deco#不带参数
@timmer  # 相当于test1 = timmer(test1)
def test1():time.sleep(3)print("in the test1")#带参数
@timmer
def test2(name,age):print("name:%s,age:%s"%(name,age))
#调用
test1()
test2("Alex",22)

  

#输出

#test1

in the test1

the func run time is 3.0010883808135986

#test2

name:Alex,age:22

the func run time is 0.0  #test2

 

4.3 执行函数有返回值

def timmer(func):  #timmer(test1) func=test1def deco(*args,**kwargs):res = func(*args,**kwargs) #这边传入函数结果赋给resreturn res   # 返回resreturn deco@timmer
def test1():  # test1 =  timmer(test1)print("in the test1")return "from the test1" #执行函数test1有返回值res = test1()
print(res)

  

#输出

in the test1

from the test1

 

 通过上面的例子,可以看出,其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。

 

4.4带参数装饰器

之前我们的装饰器都是没有带参数的,其实我们已经能解决90%的问题了,但是如果说有一种情况:就是在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?

 

#本地验证
user,passwd = "zhouqiongjie","abc123"def auth(auth_type):  #传递装饰器的参数print("auth func:",auth_type)def outer_wrapper(func):   # 将被装饰的函数作为参数传递进来def wrapper(*args,**kwargs):  #将被装饰函数的参数传递进来print("wrapper func args:",*args,**kwargs)username = input("Username:").strip()password = input("Password:").strip()if auth_type == "local":if user == username and passwd == password:print("\033[32mUser has passed authentication\033[0m")res = func(*args,**kwargs)print("--after authentication")return reselse:exit("Invalid username or password")elif auth_type == "ldap":passreturn wrapperreturn outer_wrapperdef index():print("welcome to index page")@auth(auth_type="local")  #带参数装饰器
def home():print("welcome to home page")return "from home"@auth(auth_type="ldap")   #带参数装饰器
def bbs():print("welcome  to bbs page")index()
print(home())
bbs()

  

上面的例子可以看出,执行步骤:

1)        outer_wrapper = auth(auth_type="local")

2)        home = outer_wrapper(home)

3)home()

所以这个函数的作用分别是:

1)        auth(auth_type) 传递装饰器的参数

2)        outer_wrapper(func) 把函数当做实参传递进来

3)wrapper(*args,**kwargs) 真正执行装饰的函数

  

转载于:https://www.cnblogs.com/Study-Blog/p/7400983.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/540726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c 语言bool 类型数据_C ++中的bool数据类型

c 语言bool 类型数据In C programming language, to deal with the Boolean values – C added the feature of the bool data type. A bool variable stores either true (1) or false (0) values. 在C 编程语言中&#xff0c;为了处理布尔值– C 添加了bool数据类型的功能 。…

C ++中的std :: binary_search()

binary_search()作为STL函数 (binary_search() as a STL function) Syntax: 句法&#xff1a; bool binary_search (ForwardIterator first, ForwardIterator last, const T& value);Where, 哪里&#xff0c; ForwardIterator first iterator to start of the range For…

HNUSTOJ-1437 无题

1437: 无题 时间限制: 1 Sec 内存限制: 128 MB提交: 268 解决: 45[提交][状态][讨论版]题目描述 tc在玩一个很无聊的游戏&#xff1a;每一次电脑都会给一个长度不超过10^5的字符串&#xff0c;tc每次都从第一个字符开始&#xff0c;如果找到两个相邻相一样的字符&#xff0c;…

凯撒密码pythin密码_凯撒密码术

凯撒密码pythin密码Caesar cipher is one of the well-known techniques used for encrypting the data. Although not widely used due to its simplicity and being more prone to be cracked by any outsider, still this cipher holds much value as it is amongst the fir…

MultiQC使用指导

MultiQC使用指导 官网资料文献&#xff1a;MultiQC --- summarize analysis results for multiple tools and samples in a single report参考资料一&#xff1a; 整合 fastq 质控结果的工具 简介 MultiQC 是一个基于Python的模块, 用于整合其它软件的报告结果, 目前支持以下软…

FYFG的完整形式是什么?

FYFG&#xff1a;对您的未来指导 (FYFG: For Your Future Guidance) FYFG is an abbreviation of "For Your Future Guidance". FYFG是“ For your Future Guidance”的缩写 。 It is an expression, which is commonly used in the Gmail platform. It is also wr…

WorkerMan 入门学习之(二)基础教程-Connection类的使用

一、TcpConnection类 的使用 1、简单的TCP测试 Server.php <?php require_once __DIR__./Workerman/Autoloader.php; use Workerman\Worker; $worker new Worker(websocket://0.0.0.0:80);// 连接回调 $worker->onConnect function ($connection){echo "connecti…

kotlin获取属性_Kotlin程序获取系统名称

kotlin获取属性The task is to get the system name. 任务是获取系统名称。 package com.includehelpimport java.net.InetAddress/*** Function for System Name*/fun getSystemName(): String? {return try {InetAddress.getLocalHost().hostName} catch (E: Exception) {S…

71文件类型

1.kit类型 标准的SeaJs模块文件类型&#xff0c;直接对外暴露方法。 2.units类型 依赖pageJob&#xff0c;对外暴露一个名字&#xff0c;pageJob依赖暴露的名字对模块进行初始化&#xff0c;在pageJob内部逻辑自动执行init方法&#xff1b; 由于没有对外暴露方法&#xff0c;只…

ruby 生成哈希值_哈希 Ruby中的运算符

ruby 生成哈希值In the last article, we have seen how we can carry out a comparison between two hash objects with the help of "" operator? "" method is a public instance method defined in Ruby’s library. 在上一篇文章中&#xff0c;我们看…

七牛大数据平台的演进与大数据分析实践--转

原文地址&#xff1a;http://www.infoq.com/cn/articles/qiniu-big-data-platform-evolution-and-analysis?utm_sourceinfoq&utm_mediumpopular_widget&utm_campaignpopular_content_list&utm_contenthomepage 七牛大数据平台的演进与大数据分析实践 (点击放大图像…

最大化切割段

Description: 描述&#xff1a; In this article we are going to review classic dynamic programing problem which has been featured in interview rounds of amazon. 在本文中&#xff0c;我们将回顾在亚马逊的采访轮次中已经介绍的经典动态编程问题。 Problem statemen…

响应数据传出(springMVC)

1. SpringMVC 输出模型数据概述 提供了以下几种途径输出模型数据&#xff1a; ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据 Map 及 Model: 入参为 org.springframework.ui.Model、 org.springframework.ui.ModelMap 或 java.uti…

python 字母顺序计数_计数并说出顺序

python 字母顺序计数Problem statement: 问题陈述&#xff1a; The count-and-say sequence is the sequence of integers with the first five terms as following: 计数序列是具有前五个项的整数序列&#xff0c;如下所示&#xff1a; 1 1个 11 11 21 21 1211 1211 111221 …

微信网页扫码登录的实现

为了让用户登录网站的门槛更低&#xff0c;微信扫一扫登录变得越来越广泛&#xff0c;所以最近加紧赶制的项目中有用到这个功能&#xff0c;此篇文字的出发点基于微信开放平台已经配置好域名&#xff08;80端口&#xff09;并且认证成功获得app_id和secret并有权限调用微信的接…

希尔密码_希尔密码| 网络安全

希尔密码Now, Hill Cipher is a very basic cryptographic technique which is used to convert a string into ciphertext. This technique was invented by an American Mathematician "Lester Sanders Hill". This is a polygraphic substitution cipher because …

Android 那些年,处理getActivity()为null的日子

在日常开发中的时候&#xff0c;我们经常会使用ViewPagerFragment进行视图滑动&#xff0c;在某些部分逻辑也许我们需要利用上下文Context&#xff08;例如基本的Toast&#xff09;&#xff0c;但是由于Fragment只是衣服在Activity容器的一个试图&#xff0c;如果需要拿到当前的…

设计模式状态模式uml_UML的完整形式是什么?

设计模式状态模式umlUML&#xff1a;统一建模语言 (UML: Unified Modeling Language) UML is an abbreviation of Unified Modeling Language. In the field of software engineering, it is a visual modeling language that is standard in quality. It makes it available t…

idea debug快捷键

idea的debug调试快捷键 F9 resume programe 恢复程序 AltF10 show execution point 显示执行断点 F8 Step Over 相当于eclipse的f6 跳到下一步 F7 Step Into 相当于eclipse的f5就是 进入到代码 AltshiftF7 Force Step Into 这个…

vqa mcb_MCB的完整形式是什么?

vqa mcbMCB&#xff1a;微型断路器 (MCB: Miniature Circuit Breaker) MCB is an abbreviation of "Miniature Circuit Breaker". MCB是“微型断路器”的缩写 。 It is an automatically operated electronics switch. It is designed to detect the fault in the e…