Python装饰器进阶
装饰器进阶
现在,我们已经明白了装饰器的原理。接下来,我们还有很多事情需要搞清楚。比如:装饰带参数的函数、多个装饰器同时装饰一个函数、带参数的装饰器和类装饰器。
装饰带参数函数
def foo(func): # 接收的参数是一个函数名
def bar(x, y): # 这里需要定义和被装饰函数相同的参数
print("这里是新功能...") # 新功能
func(x, y) # 被装饰函数名和参数都有了,就能执行被装饰函数了
return bar
# 定义一个需要两个参数的函数
@foo
def f1(x, y):
print("{}+{}={}".format(x, y, x+y))
# 调用被装饰函数
f1(100, 200)
输出:
这里是新功能...
100+200=300
多个装饰器
def foo1(func):
print("d1")
def inner1():
print("inner1")
return "<i>{}</i>".format(func())
return inner1
def foo2(func):
print("d2")
def inner2():
print("inner2")
return "<b>{}</b>".format(func())
return inner2
@foo1
@foo2
def f1():
return "Hello Andy"
# f1 = foo2(f1) ==> print("d2") ==> f1 = inner2
# f1 = foo1(f1) ==> print("d1") ==> f1 = foo1(inner2) ==> inner1
ret = f1() # 调用f1() ==> inner1() ==> <i>inner2()</i> ==> <i><b>inner1()</b></i> ==> <i><b>Hello Andy</b></i>
print(ret)
带参数装饰器
被装饰的函数可以带参数,装饰器同样也可以带参数。
回头看我们上面写得那些装饰器,它们默认把被装饰的函数当成唯一的参数。但是呢,有时候我们需要为我们的装饰器传递参数,这种情况下应该怎么办呢?
接下来,我们就一步步实现带参数的装饰器:
首先我们来回顾下上面的代码:
def f1(func): # f1是我们定义的装饰器函数,func是被装饰的函数
def f2(*arg, **kwargs): # *args和**kwargs是被装饰函数的参数
func(*arg, **kwargs)
return f2
从上面的代码,我们发现了什么?
我的装饰器如果有参数的话,没地方写了…怎么办呢?
还是要使用闭包函数!
我们需要知道,函数除了可以嵌套两层,还能嵌套更多层:
# 三层嵌套的函数1
def f1():
def f2():
name = "Andy"
def f3():
print(name)
return f3
return f2
嵌套三层之后的函数调用:
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> Andy
注意:在内部函数f3中能够访问到它外层函数f2中定义的变量,当然也可以访问到它最外层函数f1中定义的变量。
# 三层嵌套的函数2
def f1():
name = "Andy"
def f2():
def f3():
print(name)
return f3
return f2
调用:
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> Andy
好了,现在我们就可以实现我们的带参数的装饰器函数了:
# 带参数的装饰器需要定义一个三层的嵌套函数
def d(name): # d是新添加的最外层函数,为我们原来的装饰器传递参数,name就是我们要传递的函数
def f1(func): # f1是我们原来的装饰器函数,func是被装饰的函数
def f2(*arg, **kwargs): # f2是内部函数,*args和**kwargs是被装饰函数的参数
print(name) # 使用装饰器函数的参数
func(*arg, **kwargs) # 调用被装饰的函数
return f2
return f1
上面就是一个带参装饰器的代码示例,现在我们来写一个完整的应用:
def d(a=None): # 定义一个外层函数,给装饰器传参数--role
def foo(func): # foo是我们原来的装饰器函数,func是被装饰的函数
def bar(*args, **kwargs): # args和kwargs是被装饰器函数的参数
# 根据装饰器的参数做一些逻辑判断
if a:
print("欢迎来到{}页面。".format(a))
else:
print("欢迎来到首页。")
# 调用被装饰的函数,接收参数args和kwargs
func(*args, **kwargs)
return bar
return foo
@d() # 不给装饰器传参数,使用默认的'None'参数
def index(name):
print("Hello {}.".format(name))
@d("电影") # 给装饰器传一个'电影'参数
def movie(name):
print("Hello {}.".format(name))
if __name__ == '__main__':
index("Andy")
movie("Andy")
输出:
欢迎来到首页。
Hello Andy.
欢迎来到电影页面。
Hello Andy.
类装饰器和装饰类
类装饰器
除了用函数去装饰函数外,我们还可以使用类去装饰函数。
class D(object):
def __init__(self, a=None):
self.a = a
self.mode = "装饰"
def __call__(self, *args, **kwargs):
if self.mode == "装饰":
self.func = args[0] # 默认第一个参数是被装饰的函数
self.mode = "调用"
return self
# 当self.mode == "调用"时,执行下面的代码(也就是调用使用类装饰的函数时执行)
if self.a:
print("欢迎来到{}页面。".format(self.a))
else:
print("欢迎来到首页。")
self.func(*args, **kwargs)
@D()
def index(name):
print("Hello {}.".format(name))
@D("电影")
def movie(name):
print("Hello {}.".format(name))
if __name__ == '__main__':
index("Andy")
movie("Andy")
装饰类
我们上面所有的例子都是装饰一个函数,返回一个可执行函数。Python中的装饰器除了能装饰函数外,还能装饰类。
可以使用装饰器,来批量修改被装饰类的某些方法:
# 定义一个类装饰器
class D(object):
def __call__(self, cls):
class Inner(cls):
# 重写被装饰类的f方法
def f(self):
print("Hello Andy.")
return Inner
@D()
class C(object): # 被装饰的类
# 有一个实例方法
def f(self):
print("Hello world.")
if __name__ == '__main__':
c = C()
c.f()
</div>
Python装饰器进阶的更多相关文章
- (转)python装饰器进阶一
Python装饰器进阶之一 先看例子 网上有很多装饰器的文章,上来说半天也没让人看明白装饰器到底是个什么,究竟有什么用,我们直接来看几个例子. Python递归求斐波那契数列 def fibonacc ...
- (转)python装饰器二
Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...
- Python函数--装饰器进阶
开放封闭原则 1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代码扩展.添加新功能. 2.对修改是封 ...
- day 12 - 1 装饰器进阶
装饰器进阶 装饰器的简单回顾 装饰器开发原则:开放封闭原则装饰器的作用:在不改变原函数的调用方式的情况下,在函数的前后添加功能装饰器的本质:闭包函数 装饰器的模式 def wrapper(func): ...
- Python—装饰器详解
装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...
- day4之装饰器进阶、生成器迭代器
装饰器进阶 带参数的装饰器 # 某一种情况# 500个函数加装饰器, 加完后不想再加这个装饰器, 再过一个季度,又想加上去# 你可以设计你的装饰器,来确认是否执行 # 第一种情况 # 想要500个函数 ...
- 五分钟学会Python装饰器,看完面试不再慌
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第12篇文章,我们来看看Python装饰器. 一段囧事 差不多五年前面试的时候,我就领教过它的重要性.那时候我Pyt ...
- Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案
本文为霍格沃兹测试学院学员学习笔记. Python 装饰器简介 装饰器(Decorator)是 Python 非常实用的一个语法糖功能.装饰器本质是一种返回值也是函数的函数,可以称之为“函数的函数”. ...
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
随机推荐
- debian9使用systemd部署etcd集群
在centos上,是可以直接使用yum安装etcd的: # yum list | grep etcd etcd.x86_64 3.2.9-3.el7 @extras 但是,在debian上却没有安装包 ...
- Redis数据类型和常用命令
Redis相较于其它的数据库虽然简单,但是要熟记所有命令的用法也并非易事.一个简单的技巧是通过要操作的数据类型来将这些命令进行结构化. 数据类型和对应命令 所有存储于redis中的数据都对应于一个键值 ...
- Random Projection在k-means的应用
1. 随机投影 (Random Projection) 首先,这是一种降维方法.之前已经介绍过相对普遍的PCA的降维方法,这里介绍另一种降维方法Random Project.相比于PCA,他的优势可以 ...
- 在ubuntu server上搭建Hadoop
1. Java安装: Because everything work with java. $ sudo apt-get install openjdk-7-jdk 安装之后,可以查看java的版本信 ...
- Spark记录-org.apache.spark.sql.hive.HiveContext与org.apache.spark.sql.SQLContext包api分析
HiveContext/SQLContext val hiveContext=new HiveContext(new SparkContext(new SparkConf().setAppName(& ...
- Spark记录-Scala异常处理与文件I/O
Scala的异常处理类似许多其他语言(如Java).它不是以正常方式返回值,方法可以通过抛出异常来终止. 但是,Scala实际上并没有检查异常. 当您想要处理异常时,要像Java一样使用try {.. ...
- 数学:拓展Lucas定理
拓展Lucas定理解决大组合数取模并且模数为任意数的情况 大概的思路是把模数用唯一分解定理拆开之后然后去做 然后要解决的一个子问题是求模质数的k次方 将分母部分转化成逆元再去做就好了 这里贴一份别人的 ...
- MongoDB-3.4搭建副本集
搭建副本集 1:首先创建3台虚拟机作为配置环境 IP1:192.168.101.175 IP2:192.168.101.176 IP3:192.168.101.177 2.下载MongoDB 3.4版 ...
- javascript有关this的那些事(某渣提出的问题)
某人提出 请教下谁能解释下这个值var name = "The Window"; var object = { name: "My O ...
- Sublime Text 2 破解 on Mac
用Sublime Text 2自己打开自己的二进制文件:Sublime Text 2/Contents/MacOS/Sublime Text 2 搜索所有3342 3032 都替换成3242 3032 ...