装饰器

函数名可以干啥子
def func():
    print('永远都不要高估自己!')
print(func)  # 函数的内存地址

a = func  # 当成变量赋值给a
print(a)
a()  # 通过a变量调用函数

# 把函数当成参数
def f1(arg):
    arg()
f1(func)

# 函数可以当返回值
def f2():
    def f3():  # f3 = 函数的定义
        print('我是f3')
    return f3
ret = f2()  # 把内部定义的f3赋值给了ret
ret()  # 相当于执行了f3


def fa():
    print('fa')
def fb():
    print('fb')
def fc():
    print('fc')

list1 = [fa, fb, fc]
for i in list1:
    # i依次表示列表中每一个元素
    i()
还记得闭包撒
一个内层函数中,引用了外层函数(非全局)的变量,这个内层函数就可以成为闭包。
在Python中,我们可以使用__closure__来检测函数是否是闭包。
def func1():
    name = '张三'

    def func2():
        print(name)  # 能够访问到外层作用域的变量
    func2()
    print(func2.__closure__)  # (<cell at 0x1036c7438: str object at 0x10389d088>,)

func1()
print(func1.__closure__)  # None
问题来了,我们如何在函数外边调用函数内部的函数呢?
当然是把内部函数当成返回值返回了。
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()
使⽤闭包可以保留变量的引用。
闭包有关
装饰器
在说装饰器之前,我们先说⼀个软件设计的原则: 开闭原则, ⼜被成为开放封闭原则。
开放封闭原则是指对扩展代码的功能是开放的,但是对修改源代码是封闭的。这样的软件设计思路可以保证我们更好的开发和维护我们的代码。

概念:

  1.装饰器的实现是由闭包支撑的;

  2.装饰器本质上是⼀个python函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能;

  3.装饰器的返回值也是⼀个函数的对象,它经常用于有切面需求的场景,实现路由传参,flask的路由传参依赖于装饰器,浏览器通过url访问到装饰器的路由,从而访问视图函数获得返回的HTML页面;

应用场景:

  1.可以在外层函数加上时间计算函数,计算函数运行时间;

  2.计算函数运行次数;

  3.可以用在框架的路由传参上;

  4.插入日志,作为函数的运行日志;

  5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败;

  6.缓存,实现缓存处理;

  7.权限的校验,在函数外层套上权限校验的代码,实现权限校验;

def create_people():
    print('女娲真厉害,捏个泥吹口气就成了人!')


def a(func):
    def b():
        print('洒点水')
        func()
    return b

create_people = a(create_people)
create_people()
执行过程:
    首先访问a(create_people)
    把create_people函数赋值给了a函数的形参func,记住后续执行func的话实际上是执行了最开始传入的create_people函数。
    a函数执行过程就是一句话,返回了b函数。这个时候把b函数赋值给了create_people这个变量
    执行create_people的时候,相当于执行了b函数,先打印洒点水再执行func,也就是我们最开始传入的create_people函数
装饰器语法糖
上面的代码就是一个装饰器的雏形,Python中针对于上面的功能提供了一个快捷的写法,俗称装饰器语法糖。

使用装饰器语法糖的写法,实现同样功能的代码如下:
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 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 "<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)
多个装饰器装饰同一函数

装饰器究极进阶

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()
装饰类

 

posted @ 2018-10-31 15:58  .why  阅读(214)  评论(0)    收藏  举报
Live2D