python自动化之装饰器

时间:2021-01-26 06:25:29

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)