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)