• Python核心编程-闭包


    百度搜了一下闭包的概念:简而言之,闭包的作用就是在外部函数执行完并返回后,闭包使得收机制不会收回函数所占用的资源,因为内部函数的执行需要依赖外函数中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。
    我自己的理解,就是给自己看的,也希望大神指点:闭包在外函数返回的内涵数,内函数用到了外函数的参数,并没有在整个函数外部声明一个参数用来存执行这个方法时希望存下来的值,仅仅在声明外函数得到内涵数的句柄时将参数的值持久,外函数和内函数相互引用,而又不受外界打扰。像下面的第一段代码,在count1不被回收的时候,5的这个值是一直被count1所引用的,在count1不被垃圾回收机制回收之前一直有效,外界却改变不了这个值,如果在生命一个count2,那又是count2自己的领域,和count1完全没关系。

    在看书中的闭包的问题的时候,发现给了这样一段代码:

    def counter(start_at=0):
        count = [start_at]
        def incr():
            print count
            count[0] += 1
            return count[0]
        return incr
    count1 = counter(5)
    count1()
    结果:6

    结果和预想的一样。但是发现了一个问题,count = [start_at]这个地方是用一个数组来封装的,为什么要弄成这样,于是自己修改了一下,数组去掉

    def counter(start_at=0):
        count = start_at
        def incr():
            print count
            count += 1
            return count
        return incr
    count1 = counter(5)
    print count1()

    (关于下面问题的研究,参考     http://linluxiang.iteye.com/blog/789946   
    运行报错:UnboundLocalError: local variable 'count' referenced before assignment
    原因编译器看到a=a+1,检查右边发现了一个a的变量。编译器根据自己的规则(如果一个名字出现在参数声明,赋值语句(左边),函数声明,类声明,import语句,for语句和except语句中,这就是一个局部变量。)。编译器从头到尾看一遍,在a=a+1右边找到了a,决定他就是一个本地变量。编译器顺利赋值,但是在虚拟机运行的时候,虚拟机在计算a=a+1的时候并没有找到a的声明,于是报错。为什么数组正常,因为数组中的仅仅是一个引用。python的名字查找顺序:先local,再闭包,在global。

    在记录一下闭包和装饰器的使用:

     方法的参数也可以是方法:
    deco方法中将原方法返回去,但是这么弄发现只有第一次(deco(func))的时候才调了装饰方法。

    def deco(func):
        print 'before func'
        func()
        print 'after func'
        return func
    def func():
        print 'hello, bad_boy'
    myfunc = deco(func)
    myfunc()
    myfunc()
    结果:

      before func
      hello, bad_boy
      after func
      hello, bad_boy
      hello, bad_boy

    怎么每次都调用新方法稍后在说,先看看怎么用装饰器来完成上面的功能。

    def deco(func):
        print 'before func'
        func()
        print 'after func'
        return func
    @deco
    def func2():
        print 'hello, bad_boy, hahaha'
    func2()
    func2()
    结果
    before func
    hello, bad_boy, hahaha
    after func
    hello, bad_boy, hahaha
    hello, bad_boy, hahaha

    这里在声明func2的时候,就走了一遍deco方法,以后的每次调用都是deco中return 回来的那个方法,也就是func2。

    然后解决上面留下的问题:

    def deco1(func):
        def inner_deco():
            print 'before func'
            result = func()
            print 'after func'
            return result
        return inner_deco
    @deco1
    def func3():
        print 'hello, bad_boy, hahaha'
    func3()
    func3()
    结果:
    before func
    hello, bad_boy, hahaha
    after func
    before func
    hello, bad_boy, hahaha
    after func

    每次调用都是调用的新的方法,最主要的是,在声明方法func3的时候,也就仅仅是一个声明,并没有执行,这是因为在deco1方法中,没有传进方法的执行语句,执行语句func()在inner_deco内部,而deco1方法仅仅返回的是inner_deco句柄。这段代码其实就是相当于func = deco1(func3)。
    给方法中加上参数:

    def deco2(func):
        def inner_deco(x , y):
            print 'before func'
            result = func(x , y)
            print 'after func'
            return result
        return inner_deco
    
    @deco2
    def func4(x , y):
        print 'x is ', x
        print 'y is ', y
        return x + y
    print func4(1, 2)
    结果:
    before func
    x is 1
    y is 2
    after func
    3

    如果在写inner_deco的时候,并不知道外面会传什么参数,那就用*args, **kwargs,下面会用到。
    装饰器上也带有参数,借用《Python核心编程》中的例子

    from time import time
    
    def logged(when):
        def log(f, *args, **kargs):
            print '''Called:
        function: %s
        args: %r
        kargs: %r''' % (f, args, kargs)
    
        def pre_logged(f):
            def wrapper(*args, **kargs):
                log(f, *args, **kargs)
                return f(*args, **kargs)
            return wrapper
    
        def post_logged(f):
            def wrapper(*args, **kargs):
                now = time()
                try:
                    log(f, *args, **kargs)
                    return f(*args, **kargs)
                finally:
                    print "time delta: %s" % (time() - now)
            return wrapper
        try:
            return {"pre": pre_logged, "post": post_logged}[when]
        except KeyError, e:
            raise ValueError(e), 'must be "pre" or "post"'
                        
    @logged("post")
    def hello(name):
        print "Hello,", name
        
    hello("World!")

     如果想要在装饰器中也添加参数,也就得在包一层函数。理解一下,首先,到这里已经明白了,其实装饰器就是另外一个方法,方法中有参数,肯定得需要接受,在第一层函数中,是一定要接受装饰器参数的。装饰器的主要作用就是用来修饰一个方法,给这个方法添加一些额外的功能。一定会有一层函数来把要修饰的方法传进去,第二层函数就是来传递方法。方法传进来了还要有一层来执行这个方法。如果不用装饰器的方法(@logged("post")删掉),就是func = logged('post')(hello)      func('World!')这两句和利用装饰器的hello("World!")是等价的。

     

  • 相关阅读:
    数据库连接池系列之——c3p0
    spring配置
    flume ng系列之——flume安装
    flume ng之组件介绍
    flume ng之TailSource
    Flume-NG + HDFS + HIVE日志收集分析
    flume+hadoop
    Flume+kafka+storm+hdfs
    IOS中坐标转换
    tabBarItem是模型,只有控件才有textColor属性
  • 原文地址:https://www.cnblogs.com/badboyf/p/6123523.html
Copyright © 2020-2023  润新知