用好TensorFlow得先学好Python,Python关键字的那些俩仨事 - with,yield,generator

最近要改TensorFlow结构,在网上搜别人的代码的时候,看到了一个完全vectorize的写法的代码。他的代码中所有的内容都是用tf封装起来,以张量的形式表现的,这导致我觉得我之前根本不会用tensorflow,只用个sess.run去训练就完了。

为了改他的代码为我所用,第一步得先看懂他写的东西。然而我这个半路出家的java背景的家伙发现有很多python的概念我还是不懂。比如关键字“with”到底是干什么用的。然后我再stackoverflow上找了找解释with的的帖子,发现很多例子里有“yield”,这个是什么我又不知道。解释yield的帖子又提到了generator。。。。。心里一阵mmp,这tm又是个啥?? 这一下午,我怀着吃了狗屎一样的绝望心情,把这3个点弄会了,搭起了下一步通往理解vectorize写法的基础。在这里我把这三个关键字做个笔记,希望对别人有所帮助。

既然我是倒着看会的,那就先记录generator和yield。

Generator:

generator在java里没有对应的概念。它的特点是创建一个只能iterate一次的东西,不能被叫第二次。所以它内部不会把数值存入memory,这样做速度快,而且节省空间。比如有一个随机数数组,只需要用一次。那就没必要用数组来储存,用generator就好了。

建立generator跟建立数组差不多,区别是数组用中括号【】,而generator用小括号(),比如下面的例子:

myGenerator = (x * 2 for x in range(256))  # Generator expression

myList = [x * 2 for x in range(256)]  # List comprehension

如果不知道这种写法,在看python代码的时候就容易出问题。有时候用debug模式一步一步追看也容易看到什么东西本来刚创建出来,怎么莫名其妙的就跳出func没了??

不明真相的好奇宝宝们,欢迎把下面的代码那走自己用debug模式运行一下,你会发现第二段代码的mygenerator里面显示是个object的reference,而不是[0,1,4]。这说明generator也是个object。mygenerator只能被叫一次,虽然第一次iterate mygenerator之后你会发现这个变量所保持的object还存在,但是第二个for loop不会打印任何东西,也不会报错。因为里面的东西已经空了。

# 第一段

mylist = [x * x for x in range(3)]

for i in mylist:

    print(i)


# 第二段

mygenerator = (x*x for x in range(3))

for i in mygenerator:

    print(i)

for i in mygenerator:

    print(i)


除了面的写法,generator还可以伪装成函数的样子。如果一个函数里面有一个或几个“yield”,这个函数就是个generator。比如下面的例子:

1 def createGenerator():

2    mylist = range(3)

3    for i in mylist:

4        yield i

5        yield i*i

6 mygenerator = createGenerator() # create a generator

7 print(mygenerator) # mygenerator is an object!

8 for j in mygenerator:

9    print(i)

上面的例子里,def createGenerator()每一次会创建一个generator,所以它可以被多次调用。


Yield:

借着上面的例子,再解释一下yield。yield和generator是相辅相成的一对儿,就像炸酱面和大蒜一样~  yield其实就是方程的关键字“return”。只不过generator的return叫“yield”。不光写法不一样,yield和return的机理也有点不同。

要想明白yield,首先要明白一点,就是当创建generator被叫的时候,方程的主体不会运行。如果你在前5行代码放breakpoint的话,在第6行结束时你会发现不会停在前5行。因为他只是返回了一个generator的object给变量。真正会运行函数主体的是第8行,在for-loop iterates generator的时候。这时候程序会一直运行,直到遇到第一个yield。这是方程会做一个标记(我猜啊,再详细的没看过),然后返回yield后面的东西给for-loop里的j用,然后返回方程,从上一次标记的下一行开始继续运行。。。如果上面的代码把“yield” 改成“return”,程序就会报错,因为for-loop 不能 iterate一个数字(3.3版以前的python不允许generator里面出现return的)。如果是yield就不会报错,因为这时候iterate的是generator object,这个object会根据里面的yield会把数值一个个喂给j。


With:

这个在stack overflow上查了好久,越看越懵逼。 最后还是在CSDN大神里找到了简单的解释。只要把他的运作流程和作用写出来就好了嘛,干嘛说那么一对不相干的,越看越懵。。。

简单来说,with就是个方便书写而加入的statement(计算机科学里叫syntactic sugar),它是用来代替try-except-finally的。或者说with可以帮助写代码的人处理try-except-finally的逻辑,因为try-except-finally写不好是容易出逻辑错误的,但是用了with就可以无脑一行解决。所以with其实是一种encapsulation。

try-except-finally是用于执行可能出现exception的语句的。比如打开了文件最后要保证关闭,那么就要写好长。用with语句就一行,

with xxx [as X]:

    #执行啥啥啥

这个xxx是一个expression,expression是什么都行。如果是方程,返回的东西就赋值给as后面的变量。如果是个class,就会叫class里的__enter__函数,把它返回的值赋值给as后面的变量。整体的流程是这样的:

1,计算xxx,并获取一个上下文管理器。

2,上下文管理器的__exit__方法被保存起来用于之后的调用。

3,调用上下文管理器的__enter__方法

4,如果with表达式包含as X,那么xxx的返回值被赋值给X。

5,执行啥啥啥中的表达式

6,调永上下文管理器的__exit__方法。如果啥啥啥的执行过程中发生了一个异常导致程序退出,那么异常中的type、value、和traceback(也就是sys.exc_info()的返回值)将作为参数传递给__exit__方法,然后异常抛出在控制台。否则将传递三个None值。

那个上下文管理器在这里有解释。

这么一看是不是很清晰了? 如果你写一个自己的class,希望以后被with调用的话,那么也要加入__enter__和__exit__两个方程。总之with是个新的protocol,是用于那些需要后续处理的process的,它能够保证在执行完毕之后的清理工作可以顺利进行,并且不需要自己写一大堆繁琐的try-except-finally。如果还不明白,或者有陌生感的话,看下面的referece吧。


弄清了这些东西,现在我可以愉快的看tensorflow了~~~




generator的syntax:

https://www.cnblogs.com/hump/p/6287462.html

https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehension

generator和yield:

https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do

with:

https://blog.csdn.net/yxwb1253587469/article/details/52248565

https://blog.csdn.net/u014745194/article/details/71424909

https://blog.csdn.net/zhuhai__yizhi/article/details/78095650

https://stackoverflow.com/questions/26342769/meaning-of-with-statement-without-as-keyword

https://stackoverflow.com/questions/3012488/what-is-the-python-with-statement-designed-for

你可能感兴趣的:(用好TensorFlow得先学好Python,Python关键字的那些俩仨事 - with,yield,generator)