[Learning Python 4th edition] Part I 总结和练习

阵子在推上看到一个朋友利用博客做习题,我感觉这点子不错,一方面可以将答案公布出来跟大家互动,另一方面也是对自己的一种督促,尤其在这样一个很多干扰的时代。上个礼拜开始学习《Learn­ing Python》第四版,之前看过一些第二版的,但是因为学期项目比较多,搁置了,最近稍微有点时间,打算继续学习,可发现了第四版,由于侧重不一样,打算从头学。第四版主要把注意力放在了Python 3.X上了,相比较2.X,3.X更加优美,这也是作者强调的。如果不急于做 prac­ti­cal 的项目的话,就学习 Python 3.X。不过第四版也并不是一点不管2.X,很多不同点书中都有特殊记号标明。

在做题前,我先总结一下前三章,也就是第一部分的内容。第一章是关于Python的Q&A,主要讨论了Python的优劣势以及历史背景介绍。优势主要就是保证质量的前提下开发速度快、周期短,方便跨平台,还有很多扩展library的支持,它是个粘性很强的语言,可以和很多其它语言协同整合。语言本身也非常简单。劣势是它的运行效率比C/C++低(但是足够快,所以业界有很多案例是把算法部分用C实现,然后整体开发用Python)。有关历史的话就是讲了之所以叫Python是因为语言创建者Guido van Rossum很喜欢BBC 的 Monty Python’s Fly­ing Circus这档喜剧节目,因此在很多Python的书里有很多例子是用“spam”和“eggs”而不是“foo”和“bar”。

第二章是介绍了Python程序的运行原理。其实就是Python的Interpreter把*.py文件编译成Byte Code(*.pyc),然后再通过Python Vir­tual Machine(PVM)运行Byte Code。就如下面这幅图所示:

Python’s traditional runtime execution model

Python的implementation方法很多,比如用CPython、Jython(原项目叫JPython)、或IronPython。CPython就是Python的默认运行方式,而Jython和IronPython其实就是把上图的中间和右边的框换掉了。Jython会把python源代码译成java byte code,然后在JVM里跑,而IronPython则是把Python源码译成.Net byte code,然后在.Net框架跑。这也就为python和其他语言的协同整合提供了可能。作者还介绍了两个执行优化工具Psyco和Shedskin。Psyco会监视上图的PVM部分,将运行频率高的部分(我猜的)转化成真正的二进制机器码,这样程序速度就会越来越快,理论上说,用Psyco可以把python程序跑得和C一样快。Shedskin则是试图把python代码翻译成C++代码,然后直接用C++编译器编译。最后是有关软件的delivery,作者介绍了Frozen Binaries,它的工作原理就是把pyc文件(byte code)和PVM捆绑在一个文件里,比如exe文件。这样客户就可以不用安装PVM就跑python程序了。

第三章主要介绍了各种启动Python程序的办法,比如用命令行的python命令,用python的交互式命令行的import、reload、from以及exec()函数,用IDLE,用双击,用windows的Run.exe,等等。并且分析了各种启动方式的优劣势。这里面作者用了很多笔墨强调import、reload、from和exec()。import在软件开发中很重要,因为软件不可能是一个单独的py文件,进行过OOP同学应该是有深刻理解的。reload就是在import的基础上重新加载刚刚已经import的可能有更新的模块。exec()是在python程序里配合open()和read()将其他py文件都进来并且运行。from跟import有点像,只是它打破了import的namespace。我并不推崇用from和exec(),因为它们其实都会塌缩掉namespace,使得变量可能被改写。所以在程序里尽量还是用import吧。刚刚说到namespace的概念,其实有点其他语言基础的同学应该了解这个概念了,简单举个例子:

# file test_case.py
a = 'foo'
b = 'bar'
>>> a = 'spam'
>>> b = 'eggs'
>>> import test_case

此时a = 'spam'b = 'eggs',而test_case.py是import进来的,所以有个namespace,因此,test_case.a = 'foo', test_case.b = 'bar'。但是如果我用from:

>>> a = 'spam'
>>> b = 'eggs'
>>> from test_case import a, b

a = 'spam'b = 'eggs'就会被赋值为a = 'foo'b = 'bar'。这就是我上面提到的namespace的塌缩。也就是说test_case这个namespace被消除了,于是原先的test_case.a变成了a,test_case.b变成了b,于是源程序里面的变量a、b被偷偷换成了test_case里的a、b,这样的替换Python并不会做任何提醒,所以这非常容易使程序出错,除非你使用的变量名总是不重复。

在第三章的后半部分作者还介绍了IDLE,它是跟随Python安装的轻便IDE。其本身就是用Python写的。所以在众多优势背后有一个劣势就是不适合跑有tkinter GUI的程序以及费劲或多线程的程序,会卡壳,因为IDLE的GUI本身就是是基于python的一个标准库叫tkinter GUI toolkit的。这里我还是建议使用类似VIM这类文本编辑器就好了,如果非要debug,那eclipse+PyDev是个不错的选择。(另外,我在Mac OS X 10.6下用了IDLE,死掉的几率相当高,所以建议还是另谋出路……)

1. Inter­ac­tion. Using a sys­tem com­mand line, IDLE, or another method, start the Python inter­ac­tive com­mand line (»> prompt), and type the expres­sion "Hello World!" (includ­ing the quotes). The string should be echoed back to you.

2. Pro­grams. With the text edi­tor of your choice, write a sim­ple mod­ule file con­tain­ing the sin­gle state­ment print('Hello module world!') and store it as module1.py. Now, run this file by using any launch option you like: run­ning it in IDLE, click­ing on its file icon, pass­ing it to the Python inter­preter on the sys­tem shell’s com­mand line (e.g., python module1.py), built-in exec calls, imports and reloads, and so on. In fact, exper­i­ment by run­ning your file with as many of the launch tech­niques dis­cussed in this chap­ter as you can. Which tech­nique seems eas­i­est? (There is no right answer to this, of course.)

3. Mod­ules. Start the Python inter­ac­tive com­mand line (»> prompt) and import the mod­ule you wrote in exer­cise 2. Try mov­ing the file to a dif­fer­ent direc­tory and import­ing it again from its orig­i­nal direc­tory (i.e., run Python in the orig­i­nal direc­tory when you import). What hap­pens? (Hint: is there still a module1.pyc byte code file in the orig­i­nal directory?)

按照所说的做,我在Python inter­ac­tive com­mand line下无法再import原先的py文件。反馈为:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module1

而书后答案说是即使被移除,仍然是可以被import的,因为有module1.pyc的存在。那很显然书上的答案恐怕是疏漏了,或者是因为Python又有改版了。首先.pyc文件不在原目录下,而在原目录的“__pycache__”文件夹内,而且在文件夹内的命名也不是直接使用源代码的命名,而是变成“module1.cpython-32.pyc”。因为这些的变化,Pythen将不可以再次import被移除源码的module。当然我不排除是我的环境变量设置有误所致,如有错误望大家指正。

4. Scripts. If your plat­form sup­ports it, add the #! line to the top of your module1.py mod­ule file, give the file exe­cutable priv­i­leges, and run it directly as an exe­cutable. What does the first line need to con­tain? #! usu­ally only has mean­ing on Unix, Linux, and Unix-like plat­forms such as Mac OS X; if you’re work­ing on Win­dows, instead try run­ning your file by list­ing just its name in a DOS con­sole win­dow with­out the word “python” before it (this works on recent ver­sions of Win­dows), or via the Start→Run… dia­log box.

#!/usr/bin/env python
print('Hello module world!')
chmod +x module1.py
module1.py

5. Errors and debug­ging. Exper­i­ment with typ­ing math­e­mat­i­cal expres­sions and assign­ments at the Python inter­ac­tive com­mand line. Along the way, type the expres­sions 2 ** 500 and 1 / 0, and ref­er­ence an unde­fined vari­able name as we did in this chap­ter. What happens?

2**500可以运算

1/0报错了:

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

呼叫未申明变量的报错如下:

>>> conan
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'conan' is not defined

6. Breaks and cycles. At the Python com­mand line, type:

L = [1, 2] # Make a 2-item list
L.append(L) # Append L as a single item to itself
L # Print L

What hap­pens? In all recent ver­sions of Python, you’ll see a strange out­put that we’ll describe in the solu­tions appen­dix, and which will make more sense when we study ref­er­ences in the next part of the book. If you’re using a Python ver­sion older than 1.5.1, a Ctrl-C key com­bi­na­tion will prob­a­bly help on most plat­forms. Why do you think your ver­sion of Python responds the way it does for this code?

3.2版本的Python反馈为:

[1, 2, [...]]

我认为一方面Python很好地防止了无限循环,另一方面,结果很好地显示了那两行语言的意义(make sense)。
而课后答案则解释了python的处理原理,1.5.1版本之前显示结果是无止境的,之后的版本就够只能了。作者在答案里稍微介绍了一下python object在内存的结构。其实这一点跟Java很像,即赋值是用reference的,而不是直接在内存里复制,所以当list自加的时候就形成了指针从list尾指向头的一个无限循环,如图:

7. Doc­u­men­ta­tion. Spend at least 17 min­utes brows­ing the Python library and lan­guage man­u­als before mov­ing on to get a feel for the avail­able tools in the stan­dard library and the struc­ture of the doc­u­men­ta­tion set.
PyDoc果然来了个强大,还是个本地server,大致开了一下,东西确实不少,连管用户问取密码的都有,还看到了tkinter和http的包,应该说是想做什么都成了。不过点进去看Doc内容的时候很晕,估计熟悉一阵子就会觉得清晰了。

–EOF–

CBlog

About Conan1412

博客,好學者;開源,編程,設計,攝影,音樂,算法,人工智能,機器學習,網絡安全。