zoukankan      html  css  js  c++  java
  • 15 python 初学(闭包,函数装饰器)

    这一部分很重要,一定要透彻理解。可参考大神博客: 

    http://www.cnblogs.com/yuanchenqi/articles/5830025.html


    闭包:

    如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数被认为是闭包。

    闭包 = 函数块 + 定义函数时的环境

    inner 是内部函数,对 x 进行引用,内部函数 inner 就是一个闭包

    !!!当闭包执行完后,仍然能够保持住当前的运行环境。比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。

    # 个人理解:下面的 inner()函数不是一个闭包函数,因为他并没有使用 outer 中的外部变量。 
    #  不理解的一点:在外部直接调用 inner()函数不可行 (是因为找不到),但是执行outer(),将 inner 作为返回值给f,f在外部就可以执行。这又是因为什么呢???
    # ------------闭包有一个属性__closure__。 如果 inner 函数没有使用外部变量,那就不属于闭包的范畴。 f.__closure__返回为None。
    def outer(): x = 10 def inner(): return 5 return inner

    f
    = outer()  # f.__closure__返回为None。不是闭包
    print(f())


    # 下面的 inner()函数是一个闭包函数,因为 inner 函数和外部环境变量 x 结合在了一起。
    def outer(): x = 10 def inner():
     print(x) return 5 return inner

    # f 其实就是 inner,但此处仍然可以执行inner,就是因为inner使用了外部变量 x,在return inner的时候把x也一起捆绑给了他,让他使用。但是如果单独调用inner函数就会报错。
    f = outer()   # f.__closure__返回为:(<cell at 0x000000FB7467D768: int object at 0x00007FFB0FD7B470>,)

    print(f())

    闭包应用:装饰器

    闭包举例:棋盘。参考上面博客链接

    开放封闭原则:

    封闭原则:代码写好后不允许再进行修改

    开放原则:在不修改代码的条件下可以进行功能扩展

    装饰器:

    # 需求:打印出每个函数执行时间
    
    # 这样写的话,每个需要打印执行时间的函数都要写一遍相同的代码,代码重复。
    def foo():
        start = time.time()
        print('foo....')
        end = time.time()
        print('spend: %s' %(end - start))
    
    foo()
    
    
    # 改进方法1 :抽出相同的代码,写成函数print_time,将 foo 函数作为参数传入
    # 问题:在这种情况下,我不能调用函数本身去实现打印时间了,而是要通过print_time函数。
    # 这样如果其它地方之前是调用了这个函数来打印时间,现在改进了之后,就都要改一遍调用方式,很麻烦
    def foo():
        print('foo....')
        time.sleep(2)
    
    def print_time(f):
        start = time.time()
        f()
        end = time.time()
        print('spend: %s' % (end - start))
    
    print_time(foo)
    
    # 改进方法2:在 print_time 函数内定义一个内部函数 inner(),将 print_time 返回这个内部函数,重新赋值给函数变量 foo。
    # 这样通过直接调用 foo()就可以实现打印时间的功能了。
    # 改进方法3:更加高级一点的方法,使用装饰器(其实我觉得每次写 foo = print_time(foo) 也是一个很浪费时间的重复工作,装饰器可以解决)
    # 通过给一个函数使用装饰器,每次就可以直接通过调用函数来实现打印时间的功能了。
    
    def print_time(f):
        def inner():
            start = time.time()
            f()
            end = time.time()
            print('spend: %s' % (end - start))
    
        return inner
    
    
    @print_time   # 等价于语句:foo = print_time(foo)
    def foo():
        print('foo....')
        time.sleep(2)
    
    
    # 改进方法2 部分
    # foo = print_time(foo)
    
    foo()

    带参数的被装饰函数:即 foo 是一个有参数的函数,那么如何写这个装饰器呢?

    由浅入深,从简单的两个参数到不定长参数。

    两个参数实现举例:需要注意的地方

    1. inner(a, b)函数的参数要和foo(a, b)函数的参数保持一致。a, b 参数会自动对应 foo(a, b)的参数

    2. inner(a, b)函数内部一定要调用实现 f(a,b)函数。

    3. (我自己的理解与想象,未证实)foo 函数 使用装饰器 @print_time 时,因为 print_time 函数定义的有参数 f,因此就会自动帮我们将 foo 函数变量作为参数传入。

    def print_time(f):
        def inner(a, b):
            start = time.time()
            f(a, b)
            end = time.time()
            print('spend: %s' % (end - start))
    
        return inner
    
    
    @print_time   # 等价于语句:foo = print_time(foo)
    def foo(a, b):
        print(a + b)
        time.sleep(2)
    
    
    foo(2,3)
    
    # 5
    # spend: 2.0001039505004883

    不定长参数:

    import time
    
    def print_time(f):
        def inner(*args, **kwargs):
            start = time.time()
            f(*args, **kwargs)
            end = time.time()
            print('spend: %s' % (end - start))
    
        return inner
    
    
    @print_time   # 等价于语句:foo = print_time(foo)
    def foo(*args, **kwargs):
        sum = 0
        for i in args:
            sum += i
        print(sum)
        time.sleep(2)
    
    
    foo(2, 3, 4, 5)
    View Code

    带参数的装饰器:

    被装饰函数可以带参数,那么装饰器可以带参数吗?答案是肯定的。

    看一下实现举例(函数直接复制的):

    import time
    
    
    def time_logger(flag=0):
        def show_time(func):
            def wrapper(*args, **kwargs):
                start_time = time.time()
    
                func(*args, **kwargs)
    
                end_time = time.time()
    
                print('spend %s' % (end_time - start_time))
    
                if flag:
                    print('将这个操作的时间记录到日志中')
    
            return wrapper
    
        return show_time
    
    
    @time_logger(3)
    def add(*args, **kwargs):
        time.sleep(1)
    
        sum = 0
    
        for i in args:
            sum += i
    
        print(sum)
    
    
    add(2, 7, 5)

    在上面这个函数中,装饰器变为了 @time_logger(3),他做的主要工作有哪些呢,即他实现了一个什么功能呢?

    1. 执行time_logger(3),得到闭包变量 show_time,里面保存环境变量 flag。

    2. 执行 @show_time,相当于实现语句 add = show_time(add)。接下来的逻辑同上

    (自己的理解,期待指正):之所以先执行time_logger(3),而不是执行@time_logger(3),是因为判断出 time_logger 函数里面并没有将函数 add 传入进去,而是传入了 3。因此就先执行了time_logger(3),并返回了闭包变量 show_time。

    接下里试着执行 @show_time,发现此处没有传参数,但是 show_time 函数定义中却有一个参数 f,因此就会将函数 add 作为实参传给 f。这样就执行了 @show_time

    ???是不是将 函数add 传入后就是开始执行了装饰器语句 @show_time 

    等我理解的更深入了会再修改,目前的理解就是这样。

  • 相关阅读:
    后台服务器经典面试题
    Java英语面试题(核心知识篇)
    Java常用英语汇总(面试必备)
    字符压缩编码
    外排序
    基数排序
    Windows Server 2008 R2 部署服务
    LINUX中常用操作命令
    我的学习笔记_Windows_HOOK编程 2009-12-03 11:19
    CSDN-Code平台使用过程中的5点经验教训
  • 原文地址:https://www.cnblogs.com/mlllily/p/10260499.html
Copyright © 2011-2022 走看看