• python自动化之装饰器


    1 高阶函数

    满足下列条件之一就可成函数为高阶函数
    某一函数当做参数传入另一个函数中
    函数的返回值包含n个函数,n>0
    高阶函数示范

    def bar():
        print 'in the bar'
    def foo(func):
        res=func()
        return res
    foo(bar)
    

    foo(bar)()等价于 先bar=foo(bar) 再 bar()

    2 内嵌函数和变量作用域

    定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)

    嵌套函数示例

    def foo():         #定义函数foo(),
        m=3            #定义变量m=3;
        def bar():     #在foo内定义函数bar()
            n=4        #定义局部变量n=4
            print m+n  #m相当于函数bar()的全局变量
         bar()         #foo()函数内调用函数bar()
    

    当运行bar函数的时候,会首先找局部变量发现没有m,就会去父函数里找,发现有m,就会引用。这就是嵌套函数,而引用外部变量的嵌套函数就被称为闭包。

    3 闭包

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

    def counter(start_num=0):
        count = [start_num]
    
        def incr():
            count[0] += 1
            return count[0]
    
        return incr
    
    
    print(counter())   #1
    print(counter()()) #2
    print(counter()()) #3
    c = counter()      #4
    print(c())         #5
    print(c())         #6
    
    #####运行结果#####
    #1运行结果  <function counter.<locals>.incr at 0x0053D390> //返回incr这个函数变量在内存中的地址
    #2运行结果  1 //先运行counter函数得到incr这个函数变量,再运行incr()得到结果1    
    #3运行结果  1//因为先运行了counter函数所以会初始化start_num为0,所以结果还是1
    #5运行结果  1//把incr这个函数变量赋给c这个对象然后运行会得到incr()结果
    #6运行结果  2//此时count[0]=1,所以直接执行这个会继续使count[0]加1,所以运行结果为2。
    

    4 装饰器初识

    定义:装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。并且不会改变函数的调用方式。
    使用装饰器方法:@具有装饰功能的函数。例如下面的@foo。


    有些人说装饰器就是高阶函数+带有闭包特性的嵌套函数,但是我认为没有嵌套函数也可以写出装饰器。

    无参数和无嵌套函数装饰器

    def foo(func):  
        print 'decorator foo'  
        return func  
     
    @foo  
    def bar():  
        print 'bar'  
      
    bar()
    
    #没有嵌套函数,增加了打印"decorator foo"功能,并且没有改变函数的调用方式。这个相当于  ①先执行 bar=foo(bar)②再执行bar()因为bar()是函数调用所以foo(bar)必须有函数返回值,且是一个可调用的对象 
    

    无参装饰器

    无参装饰器是指装饰器没有参数

    import time
    def decorator(func):
            def wrapper(*args,**kwargs):
                start=time.time()
                func(*args,**kwargs)
                stop=time.time()
                print 'run time is %s ' %(stop-start)
                print timeout
            return wrapper
     
    @decorator                 //装饰器
    def test(list_test):
        for i in list_test:
            time.sleep(0.1)
            print '-'*20,i
      
     
    #decorator(test)(range(10)) 
    test(range(10)
    
    #可以看出装饰器decorator并没有参数,装饰器实际上是在函数里内嵌被装饰的函数,但是如果没有内嵌被装饰的函数,那么被装饰的函数就毫无意义。
    

    有参数的装饰器

    import time
    def timer(timeout=0):
        def decorator(func):
            def wrapper(*args,**kwargs):  #会给被装饰的函数传递参数,因为无法确定装饰器有多少参数,所以使用这个。
                start=time.time()
                func(*args,**kwargs)
                stop=time.time()
                print 'run time is %s ' %(stop-start)
                print timeout
            return wrapper
        return decorator
    @timer(2)          #装饰器的参数为2
    def test(list_test):
        for i in list_test:
            time.sleep(0.1)
            print '-'*20,i
      
    #timer(timeout=10)(test)(range(10))
    test(range(10))
    
    #装饰器timer的参数为2,@timer(2)相当于test=timer(2)(test)
    

    5 生成器

    定义:如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

    >>> L = [x * x for x in range(10)]
    >>> L
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> g = (x * x for x in range(10))
    >>> g
    <generator object <genexpr> at 0x1022ef630>
    

    如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    25
    >>> next(g)
    36
    >>> next(g)
    49
    >>> next(g)
    64
    >>> next(g)
    81
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    正确的方法是使用for循环,因为generator也是可迭代对象:

    >>> g = (x * x for x in range(10))
    >>> for n in g:
    ...     print(n)
    ...
    

    函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了

    def fib(max): #第一步
        n,a,b = 0,0,1
    
        while n < max:
            #print(b)
            yield  b
            a,b = b,a+b
    
            n += 1
    
        return 'done'
    
    
    
    #执行
    data = fib(10) #第二步
    print(data)    #第三步
    
    print(data.__next__())#第四步
    print(data.__next__())
    print("干点别的事")
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    
    
    #1第一步会定义一个函数
    #2第二步会定义一个data对象,但是此时并没有赋值给data
    #4第四步__next__()激活生成器,并开始运行生成器函数,遇到yield结束
    

    next()和send()的区别
    其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。
    需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。

    貌似很吊的生成器

    #_*_coding:utf-8_*_
    __author__ = 'Alex Li'
    
    import time
    def consumer(name):
        print("%s 准备吃包子啦!" %name)
        while True:
           baozi = yield
    
           print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
    
    
    def producer(name):
        c = consumer('A')
        c2 = consumer('B')
        c.__next__()  //这里其实就是启动一下,装饰器等价于c.send(None)下面这行也是类似
        c2.__next__()
        print("老子开始准备做包子啦!")
        for i in range(10):
            time.sleep(1)
            print("做了2个包子!")
            c.send(i)
            c2.send(i)
    
    producer("alex")
    
    通过生成器实现协程并行运算
    

    next和send源代码

    next与send函数,如下:
    static PyObject *
    gen_iternext(PyGenObject *gen)
    {
        return gen_send_ex(gen, NULL, 0);
    }
    
    
    static PyObject *
    gen_send(PyGenObject *gen, PyObject *arg)
    {
        return gen_send_ex(gen, arg, 0);
    }
    函数gen_send_ex如下:
    static PyObject *
    gen_send_ex(PyGenObject *gen, PyObject *arg, int exc)
    {
    PyThreadState *tstate = PyThreadState_GET();
    PyFrameObject *f = gen->gi_frame;
    PyObject *result;
    
    if (gen->gi_running) { // 判断生成器是否已经运行
        PyErr_SetString(PyExc_ValueError,
                        "generator already executing");
        return NULL;
    }
    if (f==NULL || f->f_stacktop == NULL) { // 如果代码块为空或调用栈为空,
    //则抛出StopIteration异常
        /* Only set exception if called from send() */
        if (arg && !exc)
            PyErr_SetNone(PyExc_StopIteration);
        return NULL;
    }
    
    if (f->f_lasti == -1) { // f_lasti=1 代表首次执行
        if (arg && arg != Py_None) { // 首次执行不允许带有参数
            PyErr_SetString(PyExc_TypeError,
                            "can't send non-None value to a "
                            "just-started generator");
            return NULL;
        }
    } else {
        /* Push arg onto the frame's value stack */
        result = arg ? arg : Py_None;
        Py_INCREF(result); // 该参数引用计数+1
        *(f->f_stacktop++) = result; // 参数压栈
    }
    
    /* Generators always return to their most recent caller, not
     * necessarily their creator. */
    f->f_tstate = tstate;
    Py_XINCREF(tstate->frame);
    assert(f->f_back == NULL);
    f->f_back = tstate->frame;
    
    gen->gi_running = 1; // 修改生成器执行状态
    result = PyEval_EvalFrameEx(f, exc); // 执行字节码
    gen->gi_running = 0; // 恢复为未执行状态
    
    /* Don't keep the reference to f_back any longer than necessary.  It
     * may keep a chain of frames alive or it could create a reference
     * cycle. */
    assert(f->f_back == tstate->frame);
    Py_CLEAR(f->f_back);
    /* Clear the borrowed reference to the thread state */
    f->f_tstate = NULL;
    
    /* If the generator just returned (as opposed to yielding), signal
     * that the generator is exhausted. */
    if (result == Py_None && f->f_stacktop == NULL) {
        Py_DECREF(result);
        result = NULL;
        /* Set exception if not called by gen_iternext() */
        if (arg)
            PyErr_SetNone(PyExc_StopIteration);
    }
    
    if (!result || f->f_stacktop == NULL) {
        /* generator can't be rerun, so release the frame */
        Py_DECREF(f);
        gen->gi_frame = NULL;
    }
    
    return result;
    }
    

    6 迭代器

    我们已经知道,可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如list、tuple、dict、set、str等;

    一类是generator,包括生成器和带yield的generator function。

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

    可以使用isinstance()判断一个对象是否是Iterable对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((x for x in range(10)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False
    

    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    你可能会问,为什么list、dict、str等数据类型不是Iterator?

    这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    小结
    凡是可作用于for循环的对象都是Iterable类型;

    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    7 json & pickle 模块

    用于序列化的两个模块

    • json,用于字符串 和 python数据类型间进行转换
    • pickle,用于python特有的类型 和 python的数据类型间进行转换

    Json模块提供了四个功能:dumps、dump、loads、load

    pickle模块提供了四个功能:dumps、dump、loads、load

    Json模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load

    pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不仅可以序列化字典,列表...还可以把一个程序,一个类给序列化掉)

    import json
    
    #loads  #-->  内部必须是双引号
    #dumps --loads  (对现有的一个操作)
    
    s = '{"desc":"invilad-citykey", "status":1002}'  
    l = [11,22,33,44]  
      
    result = json.loads(s)  
    print(result,type(result))  
    result = json.dumps(l)  
    print(result,type(result))  
    结果:  
    {'status': 1002, 'desc': 'invilad-citykey'} <class 'dict'>  
    [11, 22, 33, 44] <class 'str'>
    

    dump -- load(对文件的一个操作)

    s = {"desc":"invilad-citykey", "status":1002}  
    l = [11,22,33,44]  
    a = json.dump(s,open("db","w", encoding="utf-8"))  
    b = json.load(open("db","r", encoding="utf-8"))  
    print(b, type(b)) 
    
    
    import json
    s = '{"key1":"value1","key2":"value2"}'  # ==> 用json模块将字符串转化成其他数据类型,字符串里出现引号必须用双引号
    ret = json.loads(s)  # ==> loads 由字符串转其他数据类型
    print(ret,type(ret))
    
    ret = json.load(open('ethan.txt','r')) # ==> 将文档(内部是字符串格式)转换成python的其他数据类型
    print(ret,type(ret))  # ==> 文档里是字典样式的字符串
    
    l = '[11,22,3,56,75]'
    result =json.loads(l)
    print(result,type(result))
    # 总结:
    # json.loads()用于将形似字典、列表、元组的字符串,转换成字典、列表、元组
    # json.load() 用于将文档(内容是形似字典、列表、元组的字符串)转换成字典、列表、元组
    
    di = {"key1":"value1","key2":"value2"}
    ret = json.dumps(di) # ==> 将字典、列表、元组 转换成字符串格式
    print(ret,type(ret))
    
    json.dump(di,open('ethan.txt','a+'))  # ==> 将字典、元组、列表转换成字符串格式并写入文档
    
    import pickle
    
    d = {'name':'ethan','age':28}
    ret = pickle.dumps(d) # ==> pickle将字典、元组、列表转换成二进制
    print(ret,type(ret))
    
    l = [11,22,3,45,54]
    res = pickle.dumps(l)
    print(res)
    
    pickle.dump(d,open('ethan.txt','ab')) # ==> 将字典、元组、列表转换成二进制写入文档
    
    # 注意 dump load 不要一起运行,会报错,一步一步来
    
    f = open('ethan.txt','rb')
    r = pickle.loads(f.read()) # ==> 将二进制转换成字典、列表、元组
    print(r)
  • 相关阅读:
    状态图(Statechart Diagram)—UML图(五) .
    时序图(Sequence Diagram)—UML图(六)
    传说中的WCF(8):玩转消息协定
    SCSF 系列:利用 Smart Client Software Factory 实现 StopLight (Step By Step)
    ASP.NET MVC 3.0(四): 我要MVC潜规则之配置Routing
    传说中的WCF(14):WCF也可以做聊天程序
    部署图(Deployment Diagram)—UML图(九)
    活动图(Activity Diagram)—UML图(四)
    今天做的机试题Socket聊天程序
    UML的基本结构 .
  • 原文地址:https://www.cnblogs.com/wspblog/p/5773922.html
Copyright © 2020-2023  润新知