NOTE: The following is a short tutorial about python program, for Chinese readers who has never heard about Python. English readers please refer to python.org for Tutorial, Library Reference etc. documents, and books like O'Reilly's << Python Programming on Win32 >>, and Mark Pilgrim's << Dive into Python >> , Steve Holden's << Python Web Programming >> etc.
The contents here was first written/collected/translated around early of year 2001, posted to python forum on www.oso.com.cn (Open Source Online) . It is basically my personal study note. I'm very excited to learn Python, and wish more people, especially my China friends / fellows to know about it and use it. It is a pity that oso.com.cn closed its operation shortly, one of its chief tech is still hosting/maintaining www.freelamp.com .
On Dec. 2007, I'm glad to find back some missing parts from Shanghai Transportation University's BBS, :-), thus re-ordered them here .
缘起: 这点PYTHON学习笔记约写于2001年初, 首贴于奥索网站 / 开源在线 (www.oso.com.cn, Open Source Online), 可惜奥索最后没能活过互联网的泡沫破裂. 奥索当家的一位大哥现仍执着在: www.freelamp.com. 内容初浅, 只是给从没接触过PYTHON的小师弟小师妹们看的, 有两三个星期PYTHON经验的 的高手就不需要往下看了. 学Python学的很兴奋, "独乐乐, 与众乐乐, 孰乐?"
6,7年过去了, PYTHON 又有了不少发展. 个人觉得最有趣的是 ... for ... if ... 语句对 lambda, map, filter, reduce 的取代了. 有兴趣者应去看python.org上的英文原始资料, 及上面提及的英文书籍, 其中 <<Dive into Python>> (跳进Python) 好象已有国人译成中文, 请参看 www.linuxforum.net 的python 论坛
这里面的中文,基本都是在cxterm下用五笔输入的. (2007年底用百度居然在 上海交大BBS 找回了丢失的几篇, 一时高兴, 约略重新整理, 且抛砖于互联网的大海. )
俺这里想给大家初步介绍一下Python编程. 各位能看英文的, 最好是去看www.python.org上的其原作者Guido van Rossum 的入门介绍. (Python看成自1.6版以后就能处理Unicode的了, 故而也能很方便也处理中文了.) 俺这里只略提一二: 1)解释性语言一般都是完成任务快, 但所编的程序运行起来 有时会比编译语言编的程序(C/C++, Fortran, Pascal等) 要慢一个数量及以上. 虽然, 日常生活中仍然有许多事 俺们是不在乎它是0.0001秒内完成还是在0.1秒内完成,及 有时更在乎在短时间内完成一个很大的任务, 故而解释性 语言仍有很大市场. 更别提一般的解释性语言都可以用C/C++ 等编成模块来处理关键性的任务. 2)常用的解释性语言有Perl, Python, Lisp/Scheme, Ruby等. 其中Perl算是运用最广泛的了; 但Perl的程序不易管理大概 也是举世公认的(它的宗义即为: "总有其它方法来完成这个 任务", 从而经常每个人有每个人的风格). Python相对Perl 的这点真是简单了不少. 它比Perl年轻不少, 但用户增长速 度很快. 目前好象是说Mandrake, Debian的系统管理在用 Perl, 但大哥大红帽则在用Python. 最有名的要算Google了, 听说google里面的程序就用了很多python. 3)Python的交互式用法(可用作计算器, 或测试一个不熟悉的 函数等). 示例如下( >>>是Python的提示符 ): $python Python 2.1a1 (#4, Jan 29 2001, 20:22:47) [GCC 2.95.2 19991024 (release)] on sunos5 Type "copyright", "credits" or "license" for more information. >>> 3+4**2 19 >>> x=5 >>> y=6 >>> x/y 0 >>> x*1.0/y 0.83333333333333337 >>> 4)略解: a)变量不用先定义类型, 直接由系统识别(大多解释性语言 都这样) b)用行头空格数来标识块的起始, 省去了Perl/C/C++的{;} 没有{}匹配问题应该是省了不少头痛的. --- 这一点刚 开始会不太习惯, 熟了后真是很感谢G.V. Rossum的发明 c)没有了Perl中的$%@&等符号. (为方便, 下面我将变量的 结果放在 ==> 的右边) c.1) 字串(单引号等于双引号): a = 'Well well study' + ', Day day up!' 则: a[3] ==> 'l' a[-1] ==> '!' a[0:-1] ==> 'Well well study, Day day up' c.2) 数列(List), 相当于数组, 可以含不同类型 x = ['abc', 4, 'python'] 则: x[0] ==> 'abc' x[1] ==> 4 c.3) 字典(Dictionary), 可以用字串作指标 mathScore = { 'xah': 59, 'tinybit': 99 } 则: mathScore['tinybit'] ==> 99 d)函数, 可以用缺省参数: def score (user='tinybit', whichExam='yesterday'): if whichExam != 'yesterday': return 0 if user=='tinybit': return 99 else: return 59 于是: score() ==> 99 score('xah') ==> 59 还可以用关键词来调用: score(whichExam='tomorrow') ==> 0 e)模块, 我就不多说了 f)类(Class), 面向对象的编程, 同上. g)图形界面编程(GUI), 其缺省为基于TCL/TK上的Tkinter, 不过它的功能不是很多; 基于跨平台wxWidget库的 wxPython功能要强不少(尚未中文化, 有待各位努力). 还有基于Qt和Gtk库的, 不过Gtk好象目前还不能在Windows 上很好的运行, Qt虽能, 也很成熟, 但若在Windows上运行 它原则上要向TrollTech上贡$1,500的, 不象wxWidget完全 免费. 各位感兴趣的, 该去 python.org 当下它的入门简介, 我有80-90%的把握 that 你会喜欢它的. ############################################################# Python编程简介(二): 循环, 条件 及 函数编程工具 1) 循环 Python还有一个专门的范围函数: range. 例如: range(4) ==> [0, 1, 2, 3] range(2, 5) ==> [2, 3, 4] 它在for循环中常用.如: total = 0 for i in range(11): total += i 最后 total ==> 55 循环也可以用 while, 如: total, i = 0, 0 while i<11: total += i; i += 1 2) 条件编程是: if 条件语句 : 命令1 elif 条件语句: 命令2 else: 命令3 条件语句中相当于C的&&是and, 相当于||是or; 其中不允许赋值, 以避免C/C++中的不小心将 == 写成 = 后这一很常见的头痛. 例: total, i = 0, 0 while 1: total += i; i += 1 if i>11 or total > 55: break 3) 函数编程工具 (map/filter/reduce) 这几个工具, 可以帮你在处理数列时只调用一次函数, 从而在简 单处理大批数据时, 有可能大幅提高程度的执行速度. map(函数f, 数列l) ==> [ f(l[0]), f(l[0]), ... ] filter (f, l) ==> 只在l中剩下对f是真的值 reduce (f, l) ==> 用l中头两个数作参数调用f, 产生的结果再 ==> 与l中的第三个数作参数去调用f, 如此到底 如: def add(x,y): return x+y reduce ( add, range(1,11) ) ==> 55 def PingFang(x): return x*x map ( PingFang, range(1,5) ) ==> [1, 4, 9, 16] def JiShu (x): return x%2 == 1 filter ( JiShu, range(8) ) ==> [1, 3, 5, 7] 这三个函数用被后来的 for ... if ... 所取代的趋势 (此语法出现在PYTHON 2.x 之后的版本中): x= [3, 4, 5, 6] y = [ z for z in x if z%2== 1] ==> y = [3, 5] 4) 若想让你编的脚本能直接执行, 只要象其他Shell脚本一样, 在脚本第一 行头加上Python的路径即可. #!/PythonPath/python #或用: #!/usr/bin/env python 可以用sys.argv, len(sys.argv) 得到C/C++中的argv, argc. (若引入: from sys import argv 之后, 上面的sys.argv 就可直接写成 argv #################################################### Python编程简介(三): 模块与类 1)模块(Module) 你可以将几个函数编好放在一个文件中, 比方说你的 score.py文件中有: mathScore(), physScore(), chemScore() 等几个函数; 将之放在当前目录或是PYTHON 的系统调用目录中; 于是你在当前目录中的另一个文件中, 就可以有两种方法调用: a) import score score.mathScore('xah') ... b) from score import * #或可用 from score import mathScore mathScore('xah') ... 第二种方法引入模块后,调用时就不要前缀score了; 当然 第二种调用方法容易重名. 总之, Python的模块编写是很 简单的. (稍麻烦一点的是你想用C/C++来编一个Python模 块.) 我自己就把常用的物理常数编在physConst.py中,将 之放到Python安装目录下. 在简介(二)中我提到过from sys import argv之事, 因为 Python作为缺省已经以"import sys"的形式引入了sys 模 块, 所以你可以直接把 sys.argv 当作C/C++中 main(int argc, char *argv[]) 里的 argv 来用; 也可用"from sys import argv"再引入 一次, 于是后面皆可直接呼之为 argv 模块文件的第一个有效行(非注释行)可以是一行字串, 或 是夹在""" 与 """之间的若干行文字.(同样, 单引号等于 双引号: ''' == """, 但必须配对 .) 函数的定义也一样. 于是日后别人或自己在调用该函数/模块时, 即可用: print 该函数/模块.__doc__ 来看这个函数/模块的说明. 2)类(Class) class MyClass(父类): #从父类遗传一个子类: MyClass def method1(self, ...): ... def mether2(self, ...): self.method1() ... 类的实体化(instantiation)和调用为: c = MyClass(...) c.method1(...) c.method2(...) 这里有方法(method)和函数(function)的区别在: 对象.方法(参数) 相当于调用如下函数: 类.方法(对象, 参数) 所以在类的函数定义中, 第一个参数皆为对象. 通常情况下, 都将其 写为self. self本身并无实义, 这只是Python的习惯, 可能老的类浏 览软件会依靠self. 实体化后的调用, 如上面 c.method1(...) 就是 方法调用, 故不需要写出第一个对象参数. self还有一个用法. 因Python没有用Private/Public/Protected, 它 的函数中定义的变量都是局部变量. 当在一个函数之内调用类的变量 或函数时要加上self. 如: class My: x = 3 def PingFang(self): return self.x ** 2 def LiFang(self): return self.PingFang() * self.x 于是当实体化: m = My() 后, m.PingFang() ==> 9 m.LiFang() ==> 27 当在一个函数中调用另外一个函数的变量时, 两个函数中的变量都加 上"self." 通常还可定义一个相当于C++中类的初始构造器(constructor)的一个 函数: def __init__(self, ...): ... 类中的变量通常是不能隐形的, 在类的定义之外还可任意改变, 甚至 添加新变量, 与删除新变量. 但__AA_形式的变量是特别的 ( 前头最 少两个下划线, 尾部最多一个下划线.), 它相当于C++中的Protected 变量. Python的类有点绕脑筋, 不过各位若去看看它的图形界面编程(GUI),就 会发现它还是蛮有用和蛮好用的. 感兴趣的, 可以到 python.org 和 wxPython.org上走走. 诡计之一: 对简单的任务, 本人通常将之写为非面向对象的, :-) ################################################################ Python编程简介(四) 图象界面编程 (GUI) 对类的了解可以在其图象界面编程(GUI)中得以深入. 我自己当初 学C时就对它的画图功能十分感兴趣,曾用之画过不少数学解集, 如混沌学中气象预报的Lorentz方程 (正发现网上有Java展示: http://www.wcinet.net/~joshw/na/chaos/Lorentz.html 和分形学中的Mandelbrot集合等. 所以对Python的图象编程也趣 味盎然. Python的GUI主要是与其它己存在图形库的结合. 目前主要有:基 于Tcl/Tk库的Tkinter,基于wxWidget库的wxPython,基于Qt/Kde 库的PyQt/PyKde和基于Gtk+/Gnome库的PyGtk/PyGnome.其他还有 基于FlTk库的FlPy, 基于Fox库的FoxPy等. Python自带了Tkinter,其编程之简单也基本类似Tcl/Tk, 但Tk的 对象不很丰富,所产生的图形也不是很漂亮.我曾看到网上有人比 较过wxPython与Tkinter的画点速度,发现wxPython要快一个数量 级.这可能是因Tkinter不仅要调用Python还得用Tcl两个脚本语 言之故.对于PyQt,我没能在我的Mandrake7.2上编译成功,所以就 没学它了, 不过据说也拥有稍小于wxPython的用户群;PyGtk目前 还不如wxPython和PyQt成熟. 下面我就略说一下Tkinter.就入门 而言, Tkinter还是值得一瞄的.而且网上有很多关于它的入门介 绍, 如: http://www.pythonware.com/library/an-introduction-to-tkinter.htm http://www.python.org/doc/life-preserver/ 其后面几章就是Tkinter的样例, 其中一个就是画Mandelbrot集的. a)Tk与Tkinter对照: Tk ==> Tkinter button .b ==> b=Button() #在根窗口中定义一个按钮 button .panel.b ==> b=Button(panel) #在面板中定义一个按钮. button .b -fg red ==> b=Button(fg="red") #设置前景色 .b configure -fg red ==> b["fg"]=red #部件产生后再配置 ==> b.config(fg="red") #第二种方法 pack .b -side left ==> b.pack(side="left") #列入右边并显示 b)最简单的例子 (一个窗口中含字串: Hello, world!) from Tkinter import * root = Tk() #产生一个根窗口 l = Label(root, text="Hello, world!")# 产生一个标识 l.pack() root.mainloop() #进入主循环, 有事件则运行, 无事就等着 c)仍是十分简单的例子(一个窗口中两个按钮:Quit和Hello) from Tkinter import * #引入Tkinter模块 class App: #定义一个类 def __init__(self, master): #类的构造器, 相当于C++的constructor frame = Frame(master) #产生一个框架 frame.pack() button = Button(frame, text="QUIT", fg="red", command=frame.quit) button.pack(side=LEFT) hi_there = Button(frame, text="Hello", command=self.say_hi) #self.say_hi是调用类的函数 say_hi() hi_there.pack(side=LEFT) def say_hi(self): print "hi there, everyone!" root = Tk() app = App(root) #上面__init__有两个参数, self只是对象 root.mainloop() 上面b), c)取自: http://www.pythonware.com/library/tkinter/introduction/ 因b)简单, c)介绍了类的用法. 在c)中我将它原来的self.button 和self.hi_there改成了button和hi_there, 因为不会在类的其它 地方调用这两个器件. 我刚才试过, 没问题的. 网上已有国人将 Oreilly 出版社出的 <<Python Programming on Win32>> 译成中文了. ####################################################################### Python编程简介(五): 杂碎小记 这里我想补充几点我的前面四篇中尚未提及但仍很有用的地方. 下面 将要出现的 ...是Python的第二提示符, 表示继续一个不完整的命令 *)Python中的每个变量都是对象 --- Python特有的对象, 并不一定 都是 "面向对象的编程" 中的对象 *)第一有效行(非解释行), 其行首必须没有空格(在Python中, 表格 键Tab 缺省是等于8个空格.) *)续行可以用行末的 '\', 注意其后必需是回车/换行; 对于包在大, 中, 小括号中的若干行, 系统都缺省作续在一起的一行. 但不能把 一个变量名分开放在两行上. 如: >>> score = {'xah':59, ... 'you':99} >>> >>> score\ ... ['xah'] 59 >>> scor\ ... e['xah'] #错误 File "", line 2 e['xah'] ^ SyntaxError: invalid syntax >>> *)交互式用法中的下划线: _ , 是上面一行命令运行的结果. 如: >>> 3+5 8 >>> _ * 7 56 *)屏幕输出 在print最后加一逗号, 就不会自动输出'\n', 而输出一空格; 也可用格式符: >>> print 'a'; print 'b' a b >>> print 'a',; print 'b' #第一句后加了一个逗号 a b >>> print '%x %s' % (11, 'abc') #多变量时须用小括号括起,见下面: Tuple b abc 再重提一下, 包在匹配的 三个单引号或三个双引号之间的字 串, 可以是多行, 相当于HTML中的 和, 即原样在 屏幕上输出. 也可对它们用格式符: >>> a = ''' pytho ... n is %s!''' % 'great' >>> a ' pytho\012n is great!' #Python用8进制显示控制字符: \n == \012 >>> print a pytho n is great! >>> Python 2.0版之后就可能用 >>stderr 来打印到标准错误输出: >>> from sys import stderr >>> print >> stderr, 'ok' ok 2.0版之前好象是用文件形式: stderr.write('ok') *)字串变数字, 数字变字串: >>> int('123') 123 >>> float('123') 123.0 >>> eval('123') #随所给字串, 既可产生整数, 也可浮点数 123 >>> str(123) #数字到字串 '123' >>> `123` #反向单引号, 还可用函数: repr(数字变量) '123' *)屏幕输入: 字串输入: s = raw_input(提示字串) 数字输入: x = input(提示字串) #相当于: eval(raw_input()) *)字串按空格或其它用户定义的分隔符来分隔. Python 2.0版以后的, 可以用: >>> a='123 321 456' >>> a.split() ['123', '321', '456'] >>> a.split('2') ['1', '3 3', '1 456'] >>> 'AA'.join( a.split('2') ) #按分隔符'AA'将字串数列合并 '1AA3 3AA1 456' 但2.0(或至少1.6 ?)以前必须要引入string模块: (也即2.0 将原本在string模块中的不少函数并入字串的方法中去了, 但不是全部): >>> from string import split, join split(a,'2') ['1', '3 3', '1 456'] >>> join ( split(a,'2'), 'AA') #同上, 但调用了函数 '1AA3 3AA1 456' *)交互式下可以用dir()来看从模块中引入了什么函数与变量: >>> import math >>> dir(math) ['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'e', 'exp' , 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp ', 'log', 'log10', 'modf', 'pi', 'pow', 'sin', 'sin h', 'sqrt', 'tan', 'tanh'] 看函数/模块的说明(函数/模块中的第一行字串, 或多行位于 成对的''', """之间): >>> import math >>> print math.__doc__ This module is always available. It provides access to the mathematical functions defined by the C standard. >>> print math.sin.__doc__ sin(x) Return the sine of x. >>> *)文件 打开: f = open("My.py") #只读 f = open("My.py", "r") #只读 f = open("My.py", 'r+b')#可读可写, 二进制文件 f = open("tmpFile", 'w')#只写 读: s = f.read(字节数) #读给定字节数的内容 text = f.read() #读整个文件, 得到一字串 line = f.readline() #读一行(含换行符: '\n') lines = f.readlines() #读整个文件, 得到字串数列 #lines[-1]是最末一行(含'\n'), 等等 写: f.write('AAAbbb\n') #写一个字串 f.writelines(lines) #写字串所组成的数列 关闭: f.close() *)lambda函数 来自Lisp/Scheme, 好象是只能有一个执行句子. 可能主要是 用来简写一个函数的定义. >>> f = lambda a, b, c : (a + b ) * c >>> f(1,2,3) 9 看样子它有点类似于C/C++中的: #define max(A, B) (A)>(B) ? (A) : (B) *)空白类可以来作C中的"struct"功能: >>> class My: pass ... #其中pass是不执行任何命令. 大概是由于分号在Python ... #中不太起作用, 所以引入"pass". 而C中就可以用: ... # for (;;); ... #来"等你等到天尽头". ... >>> xah = My() >>> xah.birthDay = 1 >>> xah.birthYear = 3000 >>> xah.birthYear 3000 >>> *)错误处理 通常Python若出错误, 即会自动退出. 但你可以用 try : 可能出错语句 except 特定错误 : 出错后要执行的命令 来处理. 如: >>> 1/0 #除0错误 Traceback (most recent call last): File "", line 1, in ? ZeroDivisionError: integer division or modulo by zero >>> try: 1/0 ... except ZeroDivisionError: ... print "ok" ... ok 要多说一句的是, 如果把两个错误一起处理, 必须要用括号: try: 1/0 except (ZeroDivisionError, TypeError): print 'wrong' 若不用括号, 那是另外一种意思 (逗号后的变量变成了错误的实体): try: 1/0 except ZeroDivisionError, instance: print 'wrong', instance *)Tuple, 我在<<英华大字典>>上都没找到其译文. 马苏安的星际 译王将之译为"元组". 英文解释似乎也很强调它的有序性.这里 我就暂用"元组". Python的元组是用小括号括起来的变量, 常数, 函数等对象的组 合, 虽然有时候小括号可以省略. 如我以前给出过: total, i = 0, 0 其实就是元组赋值: (total, i) = (0, 0) *)字串和元组皆是一经产生就不可再变更, 只能另赋新值. 这也是 元组不同数列之处. 数列和字典皆可在产生之后变更. 如: >>> s="abc" >>> s[2] = 'D' #错误: Traceback (most recent call last): File " ", line 1, in ? TypeError: object doesn't support item assignment >>> t = (3, 5) >>> t[0] = 4 #错误 Traceback (most recent call last): File " ", line 1, in ? TypeError: object doesn't support item assignment 但可以用其片段来组成一个另变量: >>> s='abc' >>> s2 = s[1:-1] + 'd' >>> s2 'bd' >>> s='def' >>> s 'def' 元组中的可变更变量也可另赋值: >>> x, y = 3, 5 >>> a=(x,y) >>> a (3, 5) >>> y=4 >>> a (3, 5) >>> x,y=3, [4,5] >>> z=x, y >>> z (3, [4, 5]) >>> y[0] = 2 >>> z (3, [2, 5]) >>> *)赋值, 跟能常所说的(如C/C++)赋值有些不一样. 变量赋给变量时, 只是增加了对同一对象的参考(Reference), 而不产生新对象. 如: >>> l=[3,4] >>> l2=l >>> l[0]=-1 >>> l [-1, 4] >>> l2 [-1, 4] 而我发现用l2 = l[0:]就不一样: >>> l=[3,4] >>> l2=l[0:] >>> l[0]=-1 >>> l [-1, 4] >>> l2 [3, 4] 但对不可变更的对象, 因其不可变更, 或只能另赋新值, 所以 只表现为常见的"赋值即产生新对象"的样子: >>> s='abc' >>> ss = s >>> ss 'abc' >>> s='345' >>> s '345' >>> ss 'abc' 本节结语: 希望大家不要被我绕糊涂了, 我只是想提供一些Python 与通常的语言, 如C/C++, 相比而不同之处. 总的来说, Python还是 蛮好用的. wxPython作者, 罗宾 . 邓恩 (Robin Dunn) 就在网上说, 他个人习惯是一切编程皆用Python; 即使真需要速度, 他也将C/C++ 编成Python模块. 到此为止对Python还很感兴趣的同志, 可以去当Python的库参考了. (mpage可以4合1, 加上正反面打印, python.org上的400页库参考 你50张纸就行了, 只要你视力不是太弱可以看小号字体, :-) ) ##################################################################3### Python编程简介(六): pygtk 的图象编程 我最近又稍学了点pygtk, 贴出来给大家分享. 我去学pygtk的原因是, 我发现在我的机子上pygtk比wxpython在 启动时要几乎快上一倍(1-2秒相对于2-5秒); 并且我发现一个简 单的按钮显示, wxpython的程序运行起来要占约10-12M内存, 而 pygtk的只要约5-6M. 后来我就到wxpython的用户邮件列表上发问, 那里的人还很热心 地作了解释, 说启动速度和内存是因为UNIX下的wxGtk在是对Gtk 库进行包装的结果, 并说启动之后的执行速度二者应无显蓍差别. wxWidget故而拥有很好的移植性. 还有人指出了二者其它异同. 大意是pygtk还可以使用主题色彩配置(themes), 而且gnomepy还 可以使用libglade库来简化很多编程任务; 但wxpython的小器件 (widget)还是要比目前的pygtk要丰富. 关于Gtk在MS Windows上 的运行, 有人说很稳定了, 也有人说每5分钟死一次机. 另外,我 还瞄了瞄二者用户列表上的帖子数量, 发现wxpython的约为pygtk 的两倍左右. 所以总的看来, 还是各有千秋. 大家不妨也来瞄瞄pygtk. Gtk+与Python的结合早就有人捣弄了, 不过目前最正统还是 James Henstridge做的,看网址他好象是澳大利亚人. pygtk的 主页在: http://www.daa.com.au/~james/pygtk/ 而在这里有pygnome的入门简介: http://laguna.fmedic.unam.mx/~daniel/pygtutorial/ 我下面就取其中的两个例子(也是pygtk源程序中examples/simple 目录中的两个程序)稍作解释. pygtk目前的稳定版是0.6.6; 作者说: 别看它版本还不高, 里 面的小器件还真不少, Gtk+ 1.2的玩意差不多全包括了. 咱这 就过来瞧一瞧. pygtk有两种实现. 最老早的是基于C语言的函数调用形式, 这 倒与Gtk+本身很相近; 后来采用了面向对象的形式, 感觉要简 洁不少. 请看: 1)"世界好!"的C语言调用形式 (来自pygtk源程序中的examples/simple/hello1.py) #!/usr/bin/env python #让程序可执行 from _gtk import * #引入pygtk的C函数模块, gtk前有下划线 from GTK import * #引入常用变量模块 def hello(*args): #函数定义: 屏幕显示"世界好" print "Hello World" gtk_widget_destroy(window) def destroy(*args): #退出函数定义 gtk_widget_hide(window) gtk_main_quit() gtk_init() #Gtk初始化 window = gtk_window_new(WINDOW_TOPLEVEL) #产生一个窗口 gtk_signal_connect(window, "destroy", destroy) #退出信号连接 gtk_container_set_border_width(window, 10) #边界宽度设定 button = gtk_button_new_with_label("Hello World")#产生一个按钮 gtk_signal_connect(button, "clicked", hello) #鼠标点击传到函数 gtk_container_add(window, button) #将按钮加入到窗口中 gtk_widget_show(button) #显示按钮 gtk_widget_show(window) #显示窗口 gtk_main() #等待鼠标动作 2)"世界好!"的面向对象的形式 (来自pygtk源程序中的examples/simple/hello2.py) #!/usr/bin/env python from gtk import * #引入pygtk的面向对象的模块, gtk前无下划线 def hello(*args): print "Hello World" window.destroy() def destroy(*args): window.hide() mainquit() window = GtkWindow(WINDOW_TOPLEVEL) #产生一个窗口 window.connect("destroy", destroy) #退出信号连接 window.set_border_width(10) #边界宽度设定 button = GtkButton("Hello World") #产生一个按钮 button.connect("clicked", hello) #鼠标点击传到函数 window.add(button) #将按钮加入到窗口中 button.show() #显示按钮 window.show() #显示窗口 mainloop() #等待鼠标动作 =========== 大家是不是也觉得第二种方法要简洁 ? pygtk的作者好象也是推荐 大家用第二种方式. 对大一点的程序, 照样可以用类(class)来把 它表达的很清楚. 我就不多说了, 各位感兴趣的, 可以去上面的入门介绍中看看它们 运行的屏幕取像(screenshot), 再去当下pygtk的源程序装上(很小, 300k, 也很容易装上), 并运行一下它里面examples目录下的样例, 就会很了解pygtk能干什么活了. 当你自己想用它编简单任务时,直 接在其某个样例的基础进行加工是最省事的了. #################################################################### Python编程简介(七): 图象界面编程 (GUI): 基于wxWidget的wxPython (http://www.wxpython.org) 这一节我将从O'Reilly的书中取样例. 其书名为: < >, 可译为: Win32上的Python编程. 网络上wxPython的介绍很少, 而 wxPython.org的编程简介也很不起作用. 幸运的是, oreilly.com上给了上 书的两章作样子. 其一是Python的COM(Windows的玩意)编程, 另一就是GUI, 我 找到它时很是高兴了一阵. http://www.oreilly.com/catalog/pythonwin32/chapter/ch20.html wxWidget也是跨平台的C++库, 并且不象Qt, 它完全免费, 虽然目前还没有 Gtk+/Gnome或Qt有名. 其实它差不多和Qt同年纪. 始于1992年欧洲爱丁堡 大学(University of EdinBurgh)的人工智能应用研究所的Julian Smart. (其姓氏: Smart, 如果意译, 可就是"聪明"的意思, :-) ) 详见: http://www.wxWidget.org 先装wxWidget库. 在Linux/Unix下就装其wxGtk库. 我在学校的SunOS和家 里的Linux(Mandrake7.2)上都很顺利装上了. 再装wxPython, 正常情况下, 进入展开后的wxPython目录, 运行 python setup.py build python setup.py install 就可以. 我同样也在家里和学校里顺利装成. wxWidget编程须要从wxApp延引一个你自己的类, 在其中要有一个OnInit函 数, 相当于C++的初始构造器. 并且规定OnInit最必须返回一个数: 1,0, true 或 false等. a)一个最简单例子(空空一个窗口) from wxPython.wx import * class MyApp(wxApp): def OnInit(self): frame = wxFrame(NULL, -1, "Hello from wxPython") frame.Show(true) self.SetTopWindow(frame) #将该框架设为主框架 return true app = MyApp(0) #书中这里有个参数: 0, 我还不理解, 因为按Python规矩self #是无意义的, 调用时就不用带参数了. 我试过去掉0的, 也行 app.MainLoop() 其中产生框架的一句: frame = wxFrame(...)中, NULL表示没有父框架, -1表示 是系统缺省地给一编号. 因wxPython采用事件联系编号的方法 (Qt用信号 --- 渠道). 在下一个例子中可以看明白. 第三个参数是标题信息, 会显示在窗口最上面的 条块上. b) 显示状态条和菜单, 选中菜单中的"About"后弹出一个对话框 ... ############################################################################# Python编程简介(八) CGI编程 这里想介绍一下Python的 CGI编程. Python也有办法直接嵌入到HTML文本中去的, 像PHP一样, 忘了是 modsnake还是modpython了. 不细述, 仅给出二者网址: http://modsnake.sourceforge.net/ http://www.modpython.org/ 下面就是Python 2.0的库参考中cgi模块的说明, 包括各小节的编号. 编好的CGI可执行文件在网站空间中的cgi-bin目录下. 后缀可以是 .cgi, .py甚至是.pl, 但其它则好象是不行. 修改文件属性让它可 执行. 必要时, 请修改 cgi-bin/.htaccess 文件, 详请参阅 apache. 1)引文(Introduction) 开头与其他CGI脚本一样, 要给出输出文本类型. 可以用: print "Content-Type: text/html\n" 来表示此下为HTML内容. 别忘了最后的一个'\n', 它产生一个空行, 表示文件头的结束. 没有它会出错误的. 单独一行 print 也是打 印一个空行. 之后就可打印出你所想要的东西了. 一个例子如下(我将它存为我的 OSO空间中/cgi-bin/cgiTest1.py): #!/usr/bin/env python #让该脚本使用Python环境 print "Content-Type: text/html" print print ''' 测试 看看照不照
我的第一个Python CGI''' 然后将它上传到你的OSO空间的cgi-bin目录下, 修改模式让它可执 行, 就可以用Netscape来访问它了. 2)使用cgi模块 CGI最常见的功能是用来处理表格输入. 建议使用"import cgi"而不要使用"from cgi import *"来引入该 模块, 因其为了向后(backward)兼容而在名域(namespace)中定义 了一些你不想要的东西. 最好使用FieldStorage这个类, 其它类常是出于向后兼容性考虑. 只实体化这个类一次, 不用带参数. 该类有点象Python的字典, 如 它有方法keys()来得到所有关键词, has_key()来看看它是否含有 某个关键词. 当有些HTML表格的项是空字串时会被忽略, 若你确实 想要它们, 可以在实体化FieldStorage时加上参数: keep_blank_values 第二个例子, 处理取姓名和地址的HTML表格, (我存为我OSO空间中 的/cgi-bin/cgiTest2.py): #!/usr/bin/env python print "Content-Type: text/html\n" import cgi form = cgi.FieldStorage() form_ok = 0 if form.has_key("name") and form.has_key("addr"): form_ok = 1 if not form_ok: print '嘿, 忘了输名字或地址了吧?' else: print "你的名字是:", form["name"].value print "
你的地址是:", form["addr"].value *注, 原说明书中有一行"return", 但我发现在OSO上它会引起错误, 所以将它改为上面else中的形式. 如果不用form['name'].value, 也可以用 form.getvalue ('name'), 后者还可以加带一个缺省参数. 当有好几个同名关键词的输入时, 二者皆返回一个字串数列. 如果一个表格栏代表上传文件名, 则在用value或getvalue方法时, 会将整个文件读入内存为一个字串. 这可能不是你想要的, 那你可 以用filename或file方法. 例如: fileitem = form["userfile"] if fileitem.file: #是上传文件; 数数多少行 linecount = 0 while 1: line = fileitem.file.readline() if not line: break linecount = linecount + 1 3)旧的类 SvFormContentDict 保存每个表格输入的单一值, 以字典的形式; 它假定没有同名关键词. FormContentDict, 类似上面, 但可以有同名关键词, 保存为字串 数列. FormContent, InterpFormContentDict 存在只是为了向极老的版 本兼容 4)函数 --- 当你需要更多的控制时 parse(fp) --- 在环境中或从一个文件fp中(缺省为stdin)处理请求. parse_qs(qs[, keep_blank_values, strict_parsing]) --- 处理字串请求, 返回字典 parse_qsl(qs[, keep_blank_values, strict_parsing]) --- 处理字串请求, 返回数列 parse_multipart(fp, pdict) --- 处理多部分(multipart/form-data)上传文件fp请求, --- 返回字典 parse_header(string) --- 处理MIME的头(如Content-Type)为主要值和参数字典 test() --- CGI测试. 它打印出最小HTTP的头, 和所获表格信息 print_environ() --- 打印shell环境 print_form(form) --- 打印表格 print_directory() --- 打印当前路径 print_environ_usage() --- 打印CGI所用的环境变量 escape(s[,quote]) --- 将字串s中的'&', '>', '<'转化成HTML的安全码 5)系统安全 当须要运行外部程序(如使用os.system()或os.popen()函数), 应 保证不要把来自客户的任意字串传到shell, 这可是臭名昭著的安 全漏洞. 纵然必须, 你至少也应保证客户的字串中只有字母, 连 字符, 下划线和句点. 6)安装 将编好的CGI脚本安装到cgi-bin目录下, 用755模式让他人能读能 执行. 他人要读的文件应设为644模式, 要写的设为666模式, 因 为服务器出于安全, 通常将读者设为没有任何权限的nobody . 若要引入不在当前目录下的模块, 可以在CGI脚本中加入路径名: import sys sys.path.insert(0, "/usr/local/lib/python") 7)测试 虽然许多CGI脚本通常只能在浏览器中运行, 但事前用python来运 行它一把, 也可以除去一些语法错误. 8)去臭虫 可以给它一个请求, 例如向我的OSO中/cgi-bin/cgiTest2.py发出 请求: http://poorlinux.oso.com.cn/cgi-bin/cgiTest2.py?name=xah&addr=unknown 如果产生错误404, 则是服务器找不到该cgi.py脚本; 若是其它错误, 比如500, 则是安装出了问题; 再下一步可能是调用cgi模块自带的测试函数: cgi.test() 其它的普通Python不能处理的错误的出错信息, 可能会保存服务 器的日志文件(log)中, 或被扔弃. 幸运的是, 你也可显示错误的回溯信息, 如下: #!/usr/bin/env python import sys import traceback print 'Contet-Type: text/html\n' sys.stderr = sys.stdout try: ...你的可能出错语句... except: print '\n\n
' traceback.print_exc() 若你怀疑引入traceback模块时出了问题, 甚至可以采用如下更 壮实的办法: #!/usr/bin/env python import sys sys.stderr = sys.stdout print 'Contet-Type: text/html\n' ...你的可能出错语句... 或者用 cgitb 反溯模块(traceback)来报错: import cgitb; cgitb.enable() 再或者, 在程序的开头, 早早就额外写一行: print 'Content-Type: text/html;\n' 有时候也能让本来不报错的cgi程序, 详详细细地报出来错误 -- 这是老朽向来秘而不宣的个人心得. 9)常见问题及解决办法 *)大多数HTTP服务器都缓冲CGI脚本的输出, 所以也就不能看到 CGI运行时的进展报告 *)事前总用"python script.py"来检查脚本的语法错误 *)使用外部程序时, 要确信它能被找到. 通常可以给出全路径名, 因为$PATH一般不会为在CGI脚本中设成一个很有用的值. *)当要读/写外部文件时, 要确保它们能被系统中任意用户读/写 *)不要试图给CGI脚本一个set-uid模式, 它在大多系统中不起作 用, 并且也易产生安全问题. ############################################################### Python编程简介(九): 内建类型 有了前面的介绍, 这里想给大家稍微系统化一下, 主要是介绍内建 类型, 依据 Python 2.0 的<<库参考>>中第二章. 1) None, 空白Python对象. 有点象C中的NULL 2) 布尔(Boolean)运算 与: and; 或: or; 非: not not的优先级小于非布尔算符. 如 not x == y 将被解释为 not ( x == y ) 而 a == not b 是错误句法. 3) 比较 (基本与C相同): <, <=, >, >=, ==, !=, <>, is, is not 其中<>也是不等于算符, 已过时. Guido以前在ABC语言中使用 了它, 到现在还很有些舍不得丢掉. 4) 数值算符(也和C相仿): +, -, *, /, % abs(x) 取绝对值 int(x) 取整 long(x) 转型到长整数, 好象可以无限大 float(x) 转型到浮点数 complex(实部, 虚部) 成为复数, 也可直接写 实部+虚部j c.conjugate() 复数取共轭 divmod(x, y) 产生一对 ( int(x/y), x%y ) pow(x,y), x**y 二者都是乘方 顺便说一下, 乘号也可用在字串, 数列上: >>> a='ab'*3 >>> a 'ababab' >>> x=[0]*3 #可用来初始化数列 >>> x [0, 0, 0] 5) 整数的位操作符 (与C相同): |, ^, &, <<, >>, ~ 其中, ~x 是x的反,即0,1互换 x^y 是 x 非或 (exclusive or, 不知如何译才好) y, 如: >>> x=5 #即 0000 0101 >>> x^1 4 #只剩下 0000 0100 >>> x^2 7 #产生 0000 0111 6) 序列(Sequence): 含6类 (其中内存缓冲buffer类型极少使用) 6.1) 所有序列都支持如下运算: x in s, x not in s x在与不在序列s中 s + ss s, ss相并产生一个新序列 s*n, n*s n个序列相并 s[i], s[i:j] 取出某一(几)个值 len(s), min(s), max(s) 长度, 最小值, 最大值 序列分两类: *)可变的(mutable): 数列 *)不可变的(immutable): 字串, unicode字串, 元组(tuple), xrange (没看到buffer是属可变还是不可变的, 不管了, 反正极少用.) 单一码(unicode)字串, 写成u加上普通字串, 如: u'abc' 6.2) 字串方法 (1.6版以前许多如下方法都只在string模块中) capitalize, center, count, encode, endswith, expandtabs, find, index, isalnum, isalpha, isdigit, islower, isspace, istitle, isupper, join, ljust, lower, lstrip, replace, rfind, rindex, rjust, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper 许多都是可以望文生义的; 其中以r开头的表示从字串末尾开始 查找; find找不到子字串时返回-1, 而index则报错; lstrip是 去掉串首的空格/表格键, rstrip是去串尾的(含\n), strip是串 首串尾都去; title是把串中每个单词的第一个字母大写, 象标 题一样. 想具体了解某个方法如join, 可以在Python的交互式界面下: >>> s='aa' >>> print s.join.__doc__ S.join(sequence) -> string Return a string which is the concatenation of the strings in the sequence. The separator between elements is S. 6.3) xrange() 类似range()函数, 但所产生的是xrange类型(行为类似 元组) 而不是数列. 它的好处在: 不随参数的增大而增大内存. 我今早上用range(1e6), 发现python占了17M内存, 用range(1e7) Linux差点没死过去. (我有128M物理内存.) 后来用了xrange(1e7), 哇噻, 才用2M内存. 它有一个方法, tolist() 变成数列. 6.4) 可变值的: 数列(list) l[i], l[i:j] 取一个或几个值 del l[i:j] 删除 l.append(x), l.extend(x)在尾部增加 l.count (x) 计算x出现的次数 l.index (x) x第一次出现的位置 l.insert(i, x) 在i位置插入x l.pop([i]) 等同del l[i]; 缺省i时, 除去最后一个 l.remove(x) 删除第一个x l.reverse() 次序反转 l.sort([比较函数]) (按比较函数)排序 7) 映射(Mapping)类型: 字典 (dictionary, 目前仅有此一个类型) len(d) 计算长度 d[k] = x 赋值x给关键词k d[k] 取出关键词是k的值 del d[k] 删除 d.clear() 清除所有内容 d.copy() 拷贝, 而=d只是增加一个参考(Reference) d.has_key(k) 若有关键词, 则返回1, 否则0 d.items() 产生内容为(关键词,值)的数列 d.keys() 关键词所组成的数列 d.update(b) 等于for k in b.keys(): d[k] = b[k] d.values() 值所组成的数列 d.get(k[, x]) 若有关键词k, 则给出d[k], (否则给出x) d.setdefault(k[, x]) 设置关键词k的缺省值x后, 返回x 8) 其它类型 (不细述) 模块, 类与类的实体, 函数, 方法, 源码对象, 类型对象, 省略对象(Ellipsis), 文件 其中文件有方法(很多都是与C相对应的): close, closed, fileno, flush, isatty, mode, name, read, readline, readlines, seek, softspace, tell, truncate, write, writelines ##### 到此希望大家是感觉Python功能很丰富, 而不是繁/烦死了, ^_^ ########################################################################### Python编程简介(十): 内建函数 abs (x) 取绝对值 apply (函数, 参数 [,关键词]) buffer (对象[,位移offset[, 大小]]) callable (对象) 可调用则返回1, 否则0 chr (整数) 整数变字符: chr(97) ==> 'a', 是ord()的反函数 cmp (x,y) 返回负数(xy) coerce (x,y) 产生元组(x,y), 似乎用处不大 compile (字串, 文件, 类型) complex (实部 [,虚部]) delattr (对象, 名称) dir ( [对象] ) divmod (x, y) eval (表达式 [, 全局变量 [,局部变量] ] ) execfile (文件 [, 全局变量 [,局部变量] ] ) filter (函数, 数列) float (x) getattr (对象, 名称 [, 缺省值] ) globals () hasattr (对象, 名称) hash (对象) hex (x) id (对象) input ( [提示字串] ) int (x [,基数] ) intern (字串) isinstance(对象,类) issubclass(子类, 父类) len (s) list (序列) locals () long (x) map (函数, 数列, ...) max (s [, 参数 ...]) min (s [, 参数 ...]) oct (x) open (文件名 [, 模式 [, 缓冲大小] ] ) ord (字符) pow (x, y [,z]) range ([开始值,]终止值[,步长]) raw_input([提示字串]) reduce (函数, 序列 [, 初始器initializer]) reload (模块) repr (对象) round (x[,n]) setattr (对象, 名称, 数值) slice ([开始,]终止[,步长]) str (对象) tuple (序列) type (对象) unichr (整数) unicode (字串[,编码方法[,出错时报告哪种错误]]) vars ([对象]) xrange ([起始,]终止[,步长]) zip (序列, ...) ########################################################################## Python编程简介(十一): 速度技巧 有了前面的描述, 大家差不多知道什么是Python了. 最近因为在网 上看一两篇关于Python的运行速度问题, 不给大家介绍一下心里不 踏实, :-) 一个最常见的速度陷坑(至少是俺在没看到网上这篇介绍时陷进去 过好些次的) 是: 许多短字串并成长字串时, 大家通常会用: shortStrs = [ str0, str1, ..., strN] #N+1个字串所组成的数列 longStr = '' for s in shortStrs: longStr += s 因为Python里字串是不可变的, 所以每次 longStr += s 都是将原 来的 longStr 与 str 拷贝成一个新字串, 再赋给longStr. 随着 longStr的不断增长, 所要拷贝的内容越来越长. 最后导至str0被 拷贝N+1次, str1是N次, ... . 那咋办呢 ? 咱们来看看Skip Montanaro先生的解说: http://musi-cal.mojam.com/~skip/python/fastpython.html 及可参考一下Guido van Rossum本人的: http://www.python.org/doc/essays/list2str.html 1)首先在大家应先学会怎么去找出速度瓶颈: Python自带有profile 模块: import profile profile.run ('想要检查的函数名()') 就会打印出那个函数里调用了几次其它函数, 各用了多少时间, 总共用了多少时间等信息 --- Nice ? 详请参阅<<库参考>>中的 profile模块的论述. 当然脑袋笨一点或是聪明一点的, 也可以用time模块中的time() 来显示系统时间, 减去上次的time()就是与它的间隔秒数了. 2)字串相并 就头上的例子而言, 用 : longStr =''.join(shortStrs) 立马搞定, :-) 但如果shortStrs里面不都是字串, 而包含了些数 字呢 ? 直接用join就会出错. 不怕, 这样来: for i in range( len(shortStrs) ): shortStrs[i] = str( shortStrs[i] ) longStr = ''.join(shortStrs) 也即先将数列中所有内容都转化为字串, 再用join. 对少数几个字串相并, 应避免用: all = str0 + str1 + str2 + str3 而用: all = '%s%s%s%s' % (str0, str1, str2, str3) 3)数列排序: list.sort () 你可以按特定的函数来: list.sort( 函数 ), 只要这个函数接受 两参数, 并按特定规则返回1, 0, -1就可以. --- 很方便吧? 但 会大大减慢运行速度. 下面的方法, 俺举例子来说明可能更容易 明白. 比方说你的数列是 l = ['az', 'by'], 你想以第二个字母来排序. 先取出你的关键词, 并与每个字串组成一个元组: new = map (lambda s: (s[1], s), l ) 于是new变成[('z', 'az'), ('y', 'by')], 再把new排一下序: new.sort() 则new就变成 [('y', 'by'), ('z', 'az')], 再返回每个元组中 的第二个字串: sorted = map (lambda t: t[1], new) 于是sorted 就是: ['by', 'az']了. 这里的lambda与map用得很 好. 4)循环 比如for循环. 当循环体很简单时, 则循环的调用前头(overhead) 会显得很臃肿, 此时map又可以帮忙了. 比如你想把一个长数列 l=['a', 'b', ...]中的每个字串变成大写, 可能会用: import string newL = [] for s in l: newL.append( string.upper(s) ) 用map就可以省去for循环的前头: import string newL = map (string.upper, l) Guido的文章讲得很详细. 5)局域变量 及 '.' 象上面, 若用 append = newL.append, 及换种import方法: import string append = newL.append for s in l: append (string.upper(s)) 会比在for中运行newL.append快一些, 为啥? 局域变量容易寻找. 俺自己就不比较时间了, Skip Montanaro的结果是: 基本循环: 3.47秒 去点用局域变量: 1.79秒 使用map: 0.54秒 6)try的使用 比如你想计算一个字串数列: l = ['I', 'You', 'Python', 'Perl', ...] 中每个词出现的次数, 你可能会: count = {} for s in l: if not count.has_key(s): count[s] = 0 else: count[s] += 1 由于每次都得在count中寻找是否已有同名关键词, 会很费时间. 而用try: count ={} for s in l: try: count[s] += 1 except KeyError: count[s] = 0 就好得多. 当然若经常出现例外时, 就不要用try了. 7)import语句 这好理解. 就是避免在函数定义中来import一个模块, 应全在 全局块中来import 8)大量数据处理 由于Python中的函数调用前头(overhead)比较重, 所以处理大量 数据时, 应: def f(): for d in hugeData: ... f() 而不要: def f(d): ... for d in hugeData: f(d) 这点好象对其它语言也适用, 差不多是放之四海而皆准, 不过对 解释性语言就更重要了. 9)减少周期性检查 这是Python的本征功能: 周期性检查有没有其它绪(thread)或系 统信号(signal)等要处理. 可以用sys模块中的setcheckinterval 来设置每次检查的时间间隔. 缺省是10, 即每10个虚拟指令 (virtual instruction)检查一次. 当你不用绪并且也懒得搭理 系统信号时, 将检查周期设长会增加速度, 有时还会很显著. ---编/译完毕. 看来Python是易学难精了, 象围棋? ########################################################################## Python编程简介(十二): ToDo / 未完待续 (要找工作/亲自到社会上讨钱讨饭吃了) *)global in function and class *)x=[ [0]*2 ] * 2 后, 赋值会有问题: x[0][0]=3 后, x[1][0] 也变成 3, ?! *)类定义中的同名函数与变量, 类大写/名词, 变量小写/名词, 方法小写/动词 *)常用模块(sys, os, os.path, commands, math, glob, dircache, time, sched, shutil, re cmath, xml, locale, gettext *)C/C++扩展 *)图象制作,处理, 包括captcha等: Python Imaging Libary *)网页/网络/Email: htmllib, urllib, urllib2, cookielib, email, smtplib, xml, soap *)集成开发环境: BOA, Glade 等 *)数组/矩阵处理: NumericPy *)实例介绍