前阵子在推上看到一个朋友利用博客做习题,我感觉这点子不错,一方面可以将答案公布出来跟大家互动,另一方面也是对自己的一种督促,尤其在这样一个很多干扰的时代。上个礼拜开始学习《Learning Python》第四版,之前看过一些第二版的,但是因为学期项目比较多,搁置了,最近稍微有点时间,打算继续学习,可发现了第四版,由于侧重不一样,打算从头学。第四版主要把注意力放在了Python 3.X上了,相比较2.X,3.X更加优美,这也是作者强调的。如果不急于做 practical 的项目的话,就学习 Python 3.X。不过第四版也并不是一点不管2.X,很多不同点书中都有特殊记号标明。
![]()
在做题前,我先总结一下前三章,也就是第一部分的内容。第一章是关于Python的Q&A,主要讨论了Python的优劣势以及历史背景介绍。优势主要就是保证质量的前提下开发速度快、周期短,方便跨平台,还有很多扩展library的支持,它是个粘性很强的语言,可以和很多其它语言协同整合。语言本身也非常简单。劣势是它的运行效率比C/C++低(但是足够快,所以业界有很多案例是把算法部分用C实现,然后整体开发用Python)。有关历史的话就是讲了之所以叫Python是因为语言创建者Guido van Rossum很喜欢BBC 的 Monty Python’s Flying Circus这档喜剧节目,因此在很多Python的书里有很多例子是用“spam”和“eggs”而不是“foo”和“bar”。
第二章是介绍了Python程序的运行原理。其实就是Python的Interpreter把*.py文件编译成Byte Code(*.pyc),然后再通过Python Virtual Machine(PVM)运行Byte Code。就如下面这幅图所示:

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的概念,其实有点其他语言基础的同学应该了解这个概念了,简单举个例子:
a = 'foo'
b = 'bar'
>>> b = 'eggs'
>>> import test_case
此时a = 'spam'、b = 'eggs',而test_case.py是import进来的,所以有个namespace,因此,test_case.a = 'foo', test_case.b = 'bar'。但是如果我用from:
>>> 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. Interaction. Using a system command line, IDLE, or another method, start the Python interactive command line (»> prompt), and type the expression "Hello World!" (including the quotes). The string should be echoed back to you.

2. Programs. With the text editor of your choice, write a simple module file containing the single statement print('Hello module world!') and store it as module1.py. Now, run this file by using any launch option you like: running it in IDLE, clicking on its file icon, passing it to the Python interpreter on the system shell’s command line (e.g., python module1.py), built-in exec calls, imports and reloads, and so on. In fact, experiment by running your file with as many of the launch techniques discussed in this chapter as you can. Which technique seems easiest? (There is no right answer to this, of course.)

![]()
![]()


![]()
![]()
3. Modules. Start the Python interactive command line (»> prompt) and import the module you wrote in exercise 2. Try moving the file to a different directory and importing it again from its original directory (i.e., run Python in the original directory when you import). What happens? (Hint: is there still a module1.pyc byte code file in the original directory?)
按照所说的做,我在Python interactive command line下无法再import原先的py文件。反馈为:
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 platform supports it, add the #! line to the top of your module1.py module file, give the file executable privileges, and run it directly as an executable. What does the first line need to contain? #! usually only has meaning on Unix, Linux, and Unix-like platforms such as Mac OS X; if you’re working on Windows, instead try running your file by listing just its name in a DOS console window without the word “python” before it (this works on recent versions of Windows), or via the Start→Run… dialog box.
print('Hello module world!')
module1.py
5. Errors and debugging. Experiment with typing mathematical expressions and assignments at the Python interactive command line. Along the way, type the expressions 2 ** 500 and 1 / 0, and reference an undefined variable name as we did in this chapter. What happens?
2**500可以运算
1/0报错了:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
呼叫未申明变量的报错如下:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'conan' is not defined
6. Breaks and cycles. At the Python command line, type:
L.append(L) # Append L as a single item to itself
L # Print L
What happens? In all recent versions of Python, you’ll see a strange output that we’ll describe in the solutions appendix, and which will make more sense when we study references in the next part of the book. If you’re using a Python version older than 1.5.1, a Ctrl-C key combination will probably help on most platforms. Why do you think your version of Python responds the way it does for this code?
3.2版本的Python反馈为:
我认为一方面Python很好地防止了无限循环,另一方面,结果很好地显示了那两行语言的意义(make sense)。
而课后答案则解释了python的处理原理,1.5.1版本之前显示结果是无止境的,之后的版本就够只能了。作者在答案里稍微介绍了一下python object在内存的结构。其实这一点跟Java很像,即赋值是用reference的,而不是直接在内存里复制,所以当list自加的时候就形成了指针从list尾指向头的一个无限循环,如图:

7. Documentation. Spend at least 17 minutes browsing the Python library and language manuals before moving on to get a feel for the available tools in the standard library and the structure of the documentation set.
PyDoc果然来了个强大,还是个本地server,大致开了一下,东西确实不少,连管用户问取密码的都有,还看到了tkinter和http的包,应该说是想做什么都成了。不过点进去看Doc内容的时候很晕,估计熟悉一阵子就会觉得清晰了。
–EOF–

Pingback: [Learning Python 4th Edition] Ch. 4 总结和练习 | conanBlog.me();
Pingback: [Learning Python 4th Edition] 重定义 | conanBlog.me();