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)
posted @ 2016-08-15 18:15  Dus  阅读(576)  评论(0编辑  收藏  举报