码迷,mamicode.com
首页 > 其他好文 > 详细

第四天

时间:2016-08-13 20:59:37      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:

1.装饰器  =  高阶函数+嵌套函数

本质是函数,它装饰其他函数,给其他函数添加附加功能

原则:1.不能修改被装饰的函数

   2.不能修改被装饰函数的调用方式

     3.装饰器不会修改代码的任何东西

装饰器需要的知识:

     1.函数即变量

     2.高阶函数

     3.嵌套函数

 

 

2.迭代器

列表生成式

[i*2 for i in range(10)]

3.生成器:只在调用的时候才会生成相应的数据,而且只记录当前位置,不能再调用上一个数据

c=(i*2 for i in range(10))

这时,c是一个函数对象,并没有创建一个包含10个元素的集合

     <generator object <genexpr> at 0x0000000002E6D5A0>

for i in c:

  print(i)

通过这种方法,当用到某个元素的时候,调用c将元素取出来,这种方法可以大量节省内存

【技巧:c.__next__()得到下一个元素,没有方法得到上一个元素】

他山之石

##############################################################          

生成器(显著标志是yield)

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

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

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

1
2
3
4
5
6
>>> 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>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> 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

我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

 

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

1
2
3
4
5
6
7
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return ‘done‘

注意,赋值语句:

1
a, b = b, a + b

相当于:

1
2
3
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]

但不必显式写出临时变量t就可以赋值。

上面的函数可以输出斐波那契数列的前N个数:

1
2
3
4
5
6
7
8
9
10
11
12
>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和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
技术分享

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

技术分享
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__())

#输出
<generator object fib at 0x101be02b0>
1
1
干点别的事
2
3
5
8
13
技术分享

 

#################################################################################################

3.生成器的send函数

看过limodou的文章《2.5版yield之学习心得》,并自己反复体验后,对yield有了一个全新的理解。

1. 包含yield的函数

假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。比如下面的简单的函数:

def h():     print ‘To be brave‘     yield 5
h()

可以看到,调用h()之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。

2. yield是一个表达式

Python2.5以前,yield是一个语句,但现在2.5中,yield是一个表达式(Expression),比如:

m = yield 5

表达式(yield 5)的返回值将赋值给m,所以,认为 m = 5 是错误的。那么如何获取(yield 5)的返回值呢?需要用到后面要介绍的send(msg)方法。

3. 透过next()语句看原理

现在,我们来揭晓yield的工作原理。我们知道,我们上面的h()被调用后并没有执行,因为它有yield表达式,因此,我们通过next()语句让它执行。next()语句将恢复Generator执行,并直到下一个yield表达式处。比如:

def h():     print ‘Wen Chuan‘     yield 5     print ‘Fighting!‘
c = h() c.next()

c.next()调用后,h()开始执行,直到遇到yield 5,因此输出结果:
Wen Chuan
当我们再次调用c.next()时,会继续执行,直到找到下一个yield表达式。由于后面没有yield了,因此会拋出异常:

Wen Chuan Fighting! Traceback (most recent call last):   File "/home/evergreen/Codes/yidld.py", line 11, in <module>     c.next() StopIteration

4. send(msg) 与 next()

了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做
c.next() 和 c.send(None) 作用是一样的。
来看这个例子:

def h():     print ‘Wen Chuan‘,     m = yield 5  # Fighting!     print m     d = yield 12     print ‘We are together!‘
c = h() c.next()  #相当于c.send(None) c.send(‘Fighting!‘)  #(yield 5)表达式被赋予了‘Fighting!‘

输出的结果为:
Wen Chuan Fighting!
需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。

5. send(msg) 与 next()的返回值

send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。比如yield 5,则返回 5 。到这里,是不是明白了一些什么东西?本文第一个例子中,通过for i in alist 遍历 Generator,其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。我们再延续上面的例子:

def h():     print ‘Wen Chuan‘,     m = yield 5  # Fighting!     print m     d = yield 12     print ‘We are together!‘
c = h() m = c.next()  #m 获取了yield 5 的参数值 5 d = c.send(‘Fighting!‘)  #d 获取了yield 12 的参数值12 print ‘We will never forget the date‘, m, ‘.‘, d

输出结果:
Wen Chuan Fighting!
We will never forget the date 5 . 12

6. throw() 与 close()中断 Generator

中断Generator是一个非常灵活的技巧,可以通过throw抛出一个GeneratorExit异常来终止Generator。Close()方法作用是一样的,其实内部它是调用了throw(GeneratorExit)的。我们看:

def close(self):     try:         self.throw(GeneratorExit)     except (GeneratorExit, StopIteration):         pass     else:         raise RuntimeError("generator ignored GeneratorExit") # Other exceptions are not caught

因此,当我们调用了close()方法后,再调用next()或是send(msg)的话会抛出一个异常:

Traceback (most recent call last):   File "/home/evergreen/Codes/yidld.py", line 14, in <module>     d = c.send(‘Fighting!‘)  #d 获取了yield 12 的参数值12 StopIteration


注:以上观点属于本人的个人理解,如有偏差请批评指正。谢谢!

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

    可以直接作用于for循环的对象统称为可迭代对象:Iterable
    iter(a)  可将可迭代对象a转换为迭代器
 
4.Json用来不同语言之间信息的转换传输
Json序列化与Json反序列化
Json序列化,(不能直接将字典存到文件里,需要Json序列化,这时字典变成字符串,不能调用了)
import json
info = {1:’ww’}
f = open(‘text.txt‘,‘w‘)
f.write(json.dumps(info))
f.close()
 
Json反序列化,这样字典就可以用了,
import json
 
f = open(‘text.txt‘,‘r‘)
f.read(json.loads(info))
f.close()
 
5.pickle作用等同于json,只能在python里用
pickle序列化,(不能直接将字典存到文件里,需要Json序列化,这时字典变成字符串,不能调用了)
import pickle
info = {1:’ww’}
f = open(‘text.txt‘,‘w‘)
f.write(pickle.dumps(info))  ==  
f.close()
 
pickle反序列化,这样字典就可以用了,
import pickle
 
f = open(‘text.txt‘,‘r‘)
f.read(pickle.loads(f.read()))  == pickle.load(f)
f.close()
 
 
 

 

第四天

标签:

原文地址:http://www.cnblogs.com/wt11/p/5768724.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!