Python基础之装饰器
2021-02-01 03:17
标签:开放封闭原则 快捷 完美 ror 传递 输出 写代码 直接 命名空间 此文可能是有史以来最全的关于Python装饰器的Blog了... 函数名是?个变量,但它是?个特殊的变量。与括号配合可以执?函数的变量。 查看函数名的内存地址: 首先我们来看一个例子: 理解了上面的例子,我们再看一个例子: 最后再看一个例子: 一个内层函数中,引用了外层函数(非全局)的变量,这个内层函数就可以成为闭包。 在Python中,我们可以使用__closure__来检测函数是否是闭包。 问题来了,我们如何在函数外边调用函数内部的函数呢? 当然是把内部函数当成返回值返回了。 内部函数当然还可包含其他的函数,多层嵌套的道理都是一样的。 接下来我们看下面这个例子,来更深刻的理解一下闭包的含义: 现在我们进行如下操作: 我们知道如果?个函数执?完毕,则这个函数中的变量以及局部命名空间中的内容都将会被销毁。在闭包中内部函数会引用外层函数的变量,而且这个变量将不会随着外层函数的结束而销毁,它会在内存中保留。 也就是说,闭包函数可以保留其用到的变量的引用。 答案 在说装饰器之前,我们先说?个软件设计的原则: 开闭原则, ?被成为开放封闭原则。 开放封闭原则是指对扩展代码的功能是开放的,但是对修改源代码是封闭的。这样的软件设计思路可以保证我们更好的开发和维护我们的代码。 我们先来写一个例子,模拟一下女娲造人: 吧,现在问题来了。上古时期啊,天气很不稳定,这个时候突然大旱三年。女娲再去捏人啊,因为太干了就捏不到一块儿去了,需要在捏人之前洒点水才行。 这不就搞定了么?但是呢,我们是不是违背了开放封闭原则呢?我们是添加了新的功能,但是我们是直接修改了源代码。在软件开发中我们应该对直接修改源代码是谨慎的。 比如,女娲为了防止浪费,想用剩下点泥巴捏个鸡、鸭、鹅什么的,也需要洒点水。那我们能在每个造鸡、造鸭、造鹅函数的源代码中都手动添加代码么?肯定是不现实的。 怎么办?再写一个函数不就OK了么? 不让我直接修改源代码,那我重新写一个函数不就可以了吗? 但是,你有没有想过一个问题,女娲造人也很累的,她后来开了很多分店,每家分店都是调用了之前的create_people函数造人,那么你修改了之后,是不是所有调用原来函数的人都需要修改调用函数的名称呢?很麻烦啊!!! 总结一句话就是如何在不改变函数的结构和调用方式的基础上,动态的给函数添加功能? 利用闭包函数不就可以了么? 但是,你这最后调用的是ret啊,不还是改变了调用方式么? 再往下看: 上面这段代码是不是完美解决了我们的问题呢? 看一下它的执行过程吧: 我们巧妙的使用闭包实现了,把一个函数包装了一下,然后再赋值给原来的函数名。 上面的代码就是一个装饰器的雏形,Python中针对于上面的功能提供了一个快捷的写法,俗称装饰器语法糖。 使用装饰器语法糖的写法,实现同样功能的代码如下: 如果被装饰的函数有返回值,我们应该怎么处理呢? 请看下面的示例: 被装饰的函数可以带参数,装饰器同样也可以带参数。 回头看我们上面写得那些装饰器,它们默认把被装饰的函数当成唯一的参数。但是呢,有时候我们需要为我们的装饰器传递参数,这种情况下应该怎么办呢? 接下来,我们就一步步实现带参数的装饰器: 首先我们来回顾下上面的代码: 从上面的代码,我们发现了什么? 我的装饰器如果有参数的话,没地方写了…怎么办呢? 还是要使用闭包函数! 我们需要知道,函数除了可以嵌套两层,还能嵌套更多层: 嵌套三层之后的函数调用: 注意:在内部函数f3中能够访问到它外层函数f2中定义的变量,当然也可以访问到它最外层函数f1中定义的变量。 调用: 好了,现在我们就可以实现我们的带参数的装饰器函数了: 上面就是一个带参装饰器的代码示例,现在我们来写一个完整的应用: 被装饰的函数最终都会失去本来的__doc__等信息, Python给我们提供了一个修复被装饰函数的工具。 同一个函数可以被多个装饰器装饰,此时需要注意装饰器的执行顺序。 我们除了可以使用函数装饰函数外,还可以用类装饰函数。 我们上面所有的例子都是装饰一个函数,返回一个可执行函数。Python中的装饰器除了能装饰函数外,还能装饰类。 可以使用装饰器,来批量修改被装饰类的某些方法: 举个实际的应用示例: 我们把类中的一个只读属性定义为property属性方法,只有在访问它时才参与计算,一旦访问了该属性,我们就把这个值缓存起来,下次再访问的时候无需重新计算。 Python基础之装饰器 标签:开放封闭原则 快捷 完美 ror 传递 输出 写代码 直接 命名空间 原文地址:https://www.cnblogs.com/zyling/p/12815062.html装饰器
函数名的运用
关于函数名
def func():
print(‘呵呵‘)
print(func) #
做变量
def func():
print(‘呵呵‘)
a = func # 把函数当成变量赋值给另外一个变量
a() # 通过变量a调用函数
做容器的元素
def func1():
print(‘func1‘)
def func2():
print(‘func2‘)
def func3():
print(‘func3‘)
def func4():
print(‘func4‘)
list1 = [func1, func2, func3, func4]
for i in list1:
i()
做参数
def func1():
print(‘func1‘)
def func2(arg):
print(‘start‘)
arg() # 执行传递进来的arg
print(‘end‘)
func2(func1) # 把func1当成参数传递给func2
做返回值
def func1():
print(‘这里是func1‘)
def func2():
print(‘这里是func2‘)
return func2 # 把func2当成返回值返回
ret = func1() # 调用func1,把返回值赋值给ret
ret() # 调用ret
闭包
灵魂三问
def func1():
name = ‘张三‘
def func2(arg):
print(arg)
func2(name)
func1()
def func1():
name = ‘张三‘
def func2():
print(name) # 能够访问到外层作用域的变量
func2()
func1()
def func1(name):
def func2():
print(name) # 能够访问到外层作用域的变量
func2()
func1(‘张三‘)
闭包的定义
def func1():
name = ‘张三‘
def func2():
print(name) # 能够访问到外层作用域的变量
func2()
print(func2.__closure__) # (
def func1():
name = ‘张三‘
def func2():
print(name)
return func2 # 把内部函数当成是返回值返回
ret = func1() # 把返回值赋值给变量ret
ret() # 调用内部函数
def func1():
def func2():
def func3():
print(‘func3‘)
return func3
return func2
ret1 = func1() # func2
ret2 = ret1() # func3
ret2()
def print_msg(msg):
# 这是外层函数
def printer():
# 这是内层函数
print(msg)
return printer # 返回内层函数
func = print_msg("Hello")
func()
>>> del print_msg
>>> func()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name ‘print_msg‘ is not defined
闭包面试题
# 编写代码实现func函数,使其实现以下效果:
foo = func(8)
print(foo(8)) # 输出64
print(foo(-1)) # 输出-8
def func(num):
def func2(num2):
return num2*num
return func2
foo = func(8)
print(foo(8))
print(foo(-1))
装饰器
装饰器来历
def create_people():
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
create_people()
def create_people():
print(‘洒点水‘)
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
create_people()
def create_people():
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
def create_people_with_water():
print(‘洒点水‘)
create_people()
create_people_with_water()
def create_people():
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
def a(func):
def b():
print(‘洒点水‘)
func()
return b
ret = a(create_people)
ret()
def create_people():
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
def a(func):
def b():
print(‘洒点水‘)
func()
return b
create_people = a(create_people)
create_people()
装饰器语法糖
def a(func):
def b():
print(‘洒点水‘)
func()
return b
@a # 装饰器语法糖
def create_people():
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
create_people()
装饰器进阶
装饰带返回值的函数
def foo(func): # 接收的参数是一个函数名
def bar(): # 定义一个内层函数
print("这里是新功能...") # 新功能
r = func() # 在内存函数中拿到被装饰函数的结果
return r # 返回被装饰函数的执行结果
return bar
# 定义一个有返回值的函数
@foo
def f1():
return ‘嘿嘿嘿‘
# 调用被装饰函数
ret = f1() # 调用被装饰函数并拿到结果
print(ret)
装饰带参数的函数
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)
带参数的装饰器
def f1(func): # f1是我们定义的装饰器函数,func是被装饰的函数
def f2(*arg, **kwargs): # *args和**kwargs是被装饰函数的参数
func(*arg, **kwargs)
return f2
# 三层嵌套的函数
def f1():
def f2():
name = "张三"
def f3():
print(name)
return f3
return f2
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> 张三
# 三层嵌套的函数2
def f1():
name = ‘张三‘
def f2():
def f3():
print(name)
return f3
return f2
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> 张三
# 带参数的装饰器需要定义一个三层的嵌套函数
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(‘张三‘)
movie(‘张三‘)
装饰器修复技术
def a(func):
@wraps(func)
def b():
print(‘洒点水‘)
func()
return b
@a # 装饰器语法糖
def create_people():
"""这是一个女娲造人的功能函数"""
print(‘女娲真厉害,捏个泥吹口气就成了人!‘)
create_people()
print(create_people.__doc__)
print(create_people.__name__)
多个装饰器装饰同一函数
def foo1(func):
print("d1")
def inner1():
print("inner1")
return "{}".format(func())
return inner1
def foo2(func):
print("d2")
def inner2():
print("inner2")
return "{}".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() ==> inner2() ==> inner1() ==> Hello Andy
print(ret)
装饰器终极进阶
类装饰器
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(‘张三‘)
movie(‘张三‘)
装饰类
# 定义一个类装饰器
class D(object):
def __call__(self, cls):
class Inner(cls):
# 重写被装饰类的f方法
def f(self):
print(‘Hello 张三.‘)
return Inner
@D()
class C(object): # 被装饰的类
# 有一个实例方法
def f(self):
print("Hello world.")
if __name__ == ‘__main__‘:
c = C()
c.f()
class lazyproperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
return value
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@lazyproperty
def area(self):
print(‘计算面积‘)
return math.pi * self.radius ** 2
c1 = Circle(10)
print(c1.area)
print(c1.area)