• Python 装饰器


    1. 闭包(closure)

    • 闭包是函数式编程的重要语法结构;
    • 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包;
    # 示例一:
    def test():
        print("=== in test func ===")
    
    # 调用函数
    test()
    
    # 引用函数, 直接书写函数名,表示引用函数;
    # 在 ipython 中运行 test,会输出: <function __main__.test>
    ret = test
    
    print(id(ret))
    print(id(test))
    
    ret()
    
    # 运行结果:
    === in test func ===
    4409211080
    4409211080
    === in test func ===
    
    
    # 示例二: 闭包定义
    # 定义一个函数
    def test1(number):
    
        # 在函数内部再定义一个函数,并且这个函数用到了外部函数(test1)的变量(number),
        # 那么将这个函数以及用到的一些变量称之为闭包
        def test_in(number_in):
            print("in test_in 函数, number_in is %d" % number_in)
            return number + number_in
        # 其实,这里返回的就是闭包的结果
        return test_in
    
    # 给 test1 函数赋值,这个20就是给参数 number
    # 此时, ret 指向 test_in, 也就是说, ret = test_in, number = 20
    ret = test1(20)
    
    # 注意,这里的100,其实是给参数 number_in
    print(ret(100))     # 输出 120
    
    #注意这里的200,其实是给参数 number_in
    print(ret(200))     # 输出 220
    

    2. 装饰器

    # 示例一:
    def foo():
        print('foo')
    
    foo     # 表示是函数
    foo()   # 表示执行foo函数
    
    # 示例二:
    def foo():
        print('foo')
    
    foo = lambda x:x + 1
    
    foo()   # 执行下面的lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数
    
    
    # 示例三:装饰器之前代码
    # 需求: 一个基础平台部门,负责提供底层的功能,如数据库操作,redis调用,监控API等功能
    # 其他业务部门使用基础功能时,只需调用基础平台提供的功能即可;
    ========== 基础平台提供的功能 ===========
    
    def f1():
        print("f1")
    
    def f2():
        print("f2")
    
    def f3():
        print("f3")
    
    def f4():
        print("f4")
    
    
    =========== 业务部门A 调用基础功能 ==========
    f1()
    f2()
    f3()
    f4()
    
    
    ========== 业务部门B 调用基础功能 ==========
    f1()
    f2()
    f3()
    f4()
    
    
    # 示例四:装饰器
    # 需求: 执行基础功能之前,需要先进行验证
    # 写代码的开放封闭原则:
    #       封闭: 已实现的功能代码块
    #       开放: 对扩展开放
    
    def w1(func):
        def inner():
            # 验证1
            # 验证2
            # 验证3
            func()
        return inner
    
    # @w1 等价于 f1 = w1(f1), 此时, f1 指向 (inner 指向的函数体)
    # 调用方,继续为 f1(),函数名不需要做修改(此处,为闭包)
    @w1
    def f1():
        print('f1')
    
    @w1
    def f2():
        print('f2')
    
    @w1
    def f3():
        print('f3')
    
    @w1
    def f4():
        print('f4')
    
    
    # 示例五: 装饰器扩展
    def makeBold(fn):
        print("=== bold ===")
        def wrapped():
            print("=== 1 ===")
            return "<b>" + fn() + "</b>"
        return wrapped
    
    
    def makeItalic(fn):
        print("=== italic ===")
        def wrapped():
            print("=== 2 ===")
            return "<i>" + fn() + "</i>"
        return wrapped
    
    
    @makeBold
    @makeItalic
    def test1():
        print("=== 3 ===")
        return "Hello world!"
    
    print(test1())      
    # 输出:
    # (前面两句,不调用test1()函数也会输出)
    # === italic ===
    # === bold ===
    # === 1 ===
    # === 2 ===
    # === 3 ===
    # <b><i>Hello world!</i></b>
    
    
    # 示例六: 装饰器什么时间执行
    def w1(func):
        print("=== 正在装饰 ===")
        def inner():
            print("=== 正在验证权限 ===")
            func()
        return inner
    
    # 只要Python 解释器执行到 @w1 这行代码,那么就会自动的进行装饰,
    # 而不是等到调用f1()的时候才装饰
    # 控制台会输出: === 正在装饰 ===
    @w1
    def f1():
        print("=== f1 ===")
    
    
    
    # 示例七: 被装饰的函数带有参数
    def w1(func):
        def inner(x, y):  # 如果x,y没有定义,那么会导致f1(22, 33)调用失败
            # 验证1
            func(x, y)    # 如果没有把x, y当做实参进行传递,那么,会导致调用f1(a, b)失败
        return inner
    
    @w1
    def f1(a, b):
        print('a = %d, b = %d' % (a, b))
    
    f1(22, 33)
    
    
    
    # 示例八: 被装饰的函数带有不定长参数
    def w1(func):
        def inner(*args, **kwargs):
            # 验证1
            func(*args, **kwargs)
        return inner
    
    @w1
    def f2(a, b, c):
        print('a = %d, b = %d, c = %d' % (a, b, c))
    
    @w1
    def f3(a, b, c, d):
        print('a = %d, b = %d, c = %d, d = %d' % (a, b, c, d))
    
    f2(11, 22, 33)
    f3(11, 22, 33, 44)
    
    
    # 示例九: 对带有返回值的函数装饰
    def w1(func):
        def inner():
            print("=== 执行前 ===")
            result = func()
            print("=== 执行后 ===")
            return result
    
        return inner
    
    @w1
    def f1():
        print("=== 功能代码 ===")
        return "zhangsan"
    
    name = f1()
    print("查询到的姓名: %s" % name)
    
    
    # 示例十: 通用装饰器((不)带参数,(不)带返回值)
    def w1(func):
        def inner(*args, **kwargs):
            print("=== 执行前 ===")
            result = func(*args, **kwargs)
            print("=== 执行后 ===")
            return result
    
        return inner
    
    @w1
    def f1():
        print("=== 功能代码 ===")
        return "haha"
    
    @w1
    def f1(a):
        print("=== 功能代码 ===")
        print("输入的字符为: %s" % a)
    
    
    # 示例十一: 装饰器带参数,在原有装饰器的基础上,设置外部变量
    from time import ctime, sleep
    
    def timefun_arg(pre="hello"):
        def timefun(func):
            def wrappedfunc():
                print("%s called at %s %s" % (func.__name__, ctime(), pre))
                return func()
            return wrappedfunc
        return timefun
    
    @timefun_arg("noodles")
    def foo():
        print("I am Noodles")
    
    @timefun_arg("google")
    def too():
        print("I am google")
    
    foo()
    sleep(2)
    too()
    
    # 输出:
    foo called at Sat Mar 10 17:56:06 2018 noodles
    I am Noodles
    too called at Sat Mar 10 17:56:08 2018 google
    I am google
    
    # 备注:
    # 1. 先执行timefun_arg("noodles")函数, 这个函数返回的结果是 timefun 函数的引用
    # 2. @timefun
    # 3. 使用 @timefun 对 foo 进行装饰
    

    2.1 类装饰器

    • 装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
    • 在Python中一般callable对象都是函数,但也有例外,只要某个对象重写了 __call__()方法,那么这个
      对象就是callable的;
    # 示例一: callable
    class Test():
        def __call__(self):
            print("call me!")
    
    t = Test()
    t()    
    # 输出: call me!
    # 对象(),会调用 __call__ 方法
    
    
    # 示例: 类装饰器
    class Test(object):
        def __init__(self, func):
            print("=== 初始化 ===")
            print("func name is %s" % func.__name__)
            self.__func = func
        def __call__(self):
            print("=== 装饰器中的功能 ===")
            self.__func()
    
    # 说明:
    # 1, 当有Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象,并且会把test这个函数
    # 名当做参数传递到 __init__ 方法中;
    #
    # 2, test函数相当于指向了用Test创建出来的实例对象;
    #
    # 3, 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的 __call__ 方法;
    #
    # 4. 为了能够在__call__ 方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存
    # 所以才有了 self.__func = func 这句代码,从而在调用 __call__ 方法中能够调用到test之前的函数体
    
    @Test
    def test():
        print("=== test ===")
    test()
    

    参考资料:

  • 相关阅读:
    poj3463 Sightseeing(最短路,次短路计数)
    poj3463 Sightseeing(读题很重要)
    poj3463 Sightseeing(读题很重要)
    hdu6181 Two Paths(次短路)
    hdu6181 Two Paths(次短路)
    Tyvj1293(新姿势:次短路)
    Tyvj1293(新姿势:次短路)
    10.bitset
    9.优先队列,priority_queue
    8.queue
  • 原文地址:https://www.cnblogs.com/linkworld/p/8577246.html
Copyright © 2020-2023  润新知