程序设计思想与方法
问题求解中的计算思维
第一章 计算与计算思维
1.3 初识Python
查看Mac中Python安装目录: 终端开启Python → import sys → print sys.path
添加模块到Python搜索路径: /Library/Python/2.7/site-packages
程序的执行方式: 1.直接在bash中 python /Users/liuzf/Desktop/hello.py
2.在解释器中 import hello (设置搜索路径,成功)
3.双击.py文件 (未成功,Windows可行;效果同1)
4.在IDLE中打开hellp.py然后Run Module(按F5)
注意事项: 1.续行符:行末加\,回车,继续写
2.注意缩进表示层属关系,必须
3.交互模式下定义函数,最后一行用空行来结束函数定义
4.在#后加注释内容
5.一般Python程序用main函数:
def main(): #定义
print “Hello.”
#空行结束定义
main() #调用运行
若处理汉字出现问题,则尝试在程序第一行输入 # -- coding: cp936 -*-
*任何时刻中止线程:键盘按下 Control+C 屏幕即显示 KeyboardInterrupt
控制时间: import time 然后 time.sleep(delay) 可以休眠 delay 秒第二章 用数据表示现实世界
2.1 数据和数据类型
并行赋值: x,y = 1,2
Python中的变量:动态类型化:不用int,float等声明确定变量类型
2.2 数值类型
整数类型 int: 1.除法不作四舍五入:11 / 3 = 3
2.求余数:18 % 5 = 3
3.乘方:8 ** 2 = 64
4.表达 x = x+y 可用:x += y;同理 -=,=,/=,%=
长整数类型 long: 1.自动类型转换:int和long运算,会先将int转换为long
浮点数类型 float: 1.小数点后可以没有数,表示小数位为零:1.
2.浮点型除法11.0/3.0 = 3.6666666666666665
3.浮点数之间不能用 == 比较是否相等,因为有微小误差;
而应看差值是否足够小:abs((1.2-1.0)-0.2) < 0.00000001
4.自动类型转换:混合运算,会先将int、long转换为float
5.手动类型转换 int() long() float();四舍五入 round() (得float)
数学库模块 math: 1.导入math(pi e sin acos atan log log10 exp ceil floor等):
import math
math.sqrt(16)
或
from math import sqrt
sqrt(16)
2.若要导入所有定义:
from math import *
复数类型 complex: 用j或J表示虚部,而不是i:
定义 c1 = 2 + 4j
则 print c1 得到 (2+4j)
求模 abs(c1) 得到 4.472135955
求虚实部 c1.real 得到 2.0;c1.imag 得到4.0
2.3 字符串类型 str
1.字符串类型的字面值形式:
使用单引号或双引号均可,是为了杜绝歧义错误:He said, “OK”.
转义字符 \ :使用 " ( ’ ) 则不再按界定符意义,而是普通的双(单)引号意义
2.字符串类型的操作:
索引:str[0]访问字符串中第一个字符,str[-1]访问倒数第一个字符,以此类推
切分:str[a:b]访问子串,不指定则为 0 / n :str[:10] str[:] str[5:] str[2:-2]
注意:终点位置不含,即str[i:i]为空串,第i个字符为str[i-1:i]
合并 “Good” + “Bye” ;复制 2 * ”Bye“ ;子串测试 “ok” in “cook” ;求长度 len()
* Python中字符串类型的值不能修改!name = “Tom” name[1]="i” 会报错
3.字符的机内表示:
ord(‘A’) 求A的ASCII码; chr(64) 求ASCII码64对应的字符
直接输入 ‘汉’ 或 ‘Ä’ 等,可以直接查看其在当前系统下的十六进制编码
直接输入 u’汉’ 或 u’Ä’ 等,可以直接查看其在Unicode下的编码
若要混用文字须用Unicode:print u’A\u6c49\xc4B’ 其中 \x 为十六进制数的标志
4.字符串类型与其他类型的转换:
用 eval() 计算字符串形式下的数学表达式
用 int() long() float() bool() str() 互相转换类型
5.字符串库 string (书P54):
capwords(s) count(s,sub) find(s,sub) rfind(s,sub) replace(s,sub,newsub)
2.4 布尔类型 bool
1.关系运算:
有 < > <= >= == !=(或 <>) 六种关系运算
字符串比较按照ASCII编码: “like” < “lake” “B-2” < “f-16”
2.逻辑运算:
三种:且 and 或 or 非 not :优先级 not > and > or
3.布尔代数运算定律:
分配律: a or (b and c)←→(a or b) and (a or c)
a and (b or c)←→(a and b) or (a and c)
De Morgan定律: not (a or b)←→(not a) and (not b)
not (a and b)←→(not a) or (not b)
4.Python中真假的表示与计算:
int long float 可以解释成布尔值:0为False,非0为True
字符串可以解释成布尔值:空串为False,非空为True
a和b为任何表达式: a and b : if a is False then return a, otherwise b
a or b : if a is False then return b, otherwise a
not a : if a is False then return True, otherwise False
(可通过此功能将布尔表达式用作流程控制结构(3.7))
2.5 列表和元组类型
1.列表类型 list :[ xxx , xxx , … , xxx ]
用 len() 求列表成员个数(列表的长度)
列表的成员本身也可以是列表,可以用此来表示数学中的矩阵 [ [11,22] , [33,44] ]
索引、访问子列表、合并、复制的方法,和字符串相同,注意终点位置不含
但字符串不可更改,列表却可以!修改操作: x[2]=‘change’
增加、删除操作: x=x+[‘bonus’] del(x[1:3])
range() 生成整数列表 range( <起点> , <终点> , <步长> ) ,终点位置不含!
列表作为参数传递,函数中的过程永久修改该列表!即便用了不同的参数名称!
2.元组类型 tuple :( xxx , xxx , xxx )
注意,只有一个成员的元组,仍需加上括号,否则解释为单个数值: (8,)
len()、索引、访问子元组、合并、复制的方法,和列表相同,也用方括号
但元组是不可更改(修改)的,只能引用元组变量,这一点上类似字符串
可以通过创建新的元组来迂回达到目的: t = t[ 0:2 ] + (8,)
2.6 数据的输入和输出
1.数据的输入: input(“请输入:”) 接收各种类型的表达式;
输入字符串需要加引号,否则会解释为变量名
a,b,c = input(“xxx”) 要输入 1,2,3 (而不是用空格分隔开)
而使用raw_input(“请输入:”) 则直接全体解释为一个字符串
raw_input 的优势为可以处理空回车,而 input 空回车会报错
\
2.数据的输出: print 输出空白行
print ,, 逗号为一空格,下一个 print 将换行
print ,,, 下一个 print 将不会换行
\
3.格式化输出: 字符串格式化运算: <模板字符串> % ( , … , )
格式定义符: % <宽度> . <精度> <类型字符>
(正宽度右对齐,负宽度左对齐;精度不过高)
例如: %d %f(6位小数) %.4f %s %20s %-20s(左对齐)
举例:"%s gives %s $%d." % (“Tom”,“Jack”,100)第三章 数据处理的流程控制
3.1 顺序控制结构: 开始结束椭圆,输入输出平行四边形,运算长方形,条件测试菱形
3.2 分支控制结构: 单分支结构: if <条件表达式> :
<条件语句体> ←注意此处必须缩进!!
两路分支结构: if <条件表达式> :
else:
多路分支结构: 可以使用嵌套 if-else 语句,但是容易较复杂
最好使用Python的 if-elif-else 语句:
if <条件1> :
<情形1语句体>
elif <条件2> :
<情形2语句体>
…
elif <条件n> :
<情形n语句体>
else :
<其他情形语句体>
3.3 异常处理: import xxxx 之后重新运行已成功导入的模块: reload(xxxx)
异常处理机制(保持程序结构的清晰):先 try: 然后 except IndexError: …
try :
<语句块>
except <错误类型1> :
<异常处理语句块1>
…
except <错误类型n> :
<异常处理语句块n>
except :
<默认异常处理语句块>
常见错误类型有: ValueError IndexError TypeError NameError 等
可手动抛出异常: raise ValueError (也可由 try-except 捕获)
可以带有描述信息: raise ValueError, “HaHa!!!”
用户可自定义异常:通过自定义异常类的子类,可自行命名
确保执行相应代码: try-finally (finally后的语句一定会被执行)
综上,一般形式: try-except-finally (如出错,先finally,再except)
try :
<语句块>
except <错误类型> :
<异常处理语句块>
…
except :
<默认异常处理语句块>
finally:
print “This is final!”
3.4 循环控制结构: 1. for <循环控制变量> in <序列>:
<循环体> ←注意此处必须缩进!!
例1: for i in range(10): ←计数器循环,控制循环次数
print “烦”,
或者:sum = sum + i * i 等等用法
例2: data = [‘a’ , ‘b’ , numx , numy] ←遍历数据项序列
for d in data:
print d,
输出:a b numx numy (元组用法效果相同)
例3: data = [1,2,3,4,5] ←构建序列索引,遍历并修改原列表
for i in range(len(data)):
data[i] = data[i] + 1
例4: for (x,y) in [(1,2) , (3,4) , (5,6)]: ←多个循环控制变量构成元组
print x,y
输出: 1 2
3 4
5 6
例5: for ((a,b),c) in [([1,2],3),[‘XY’,6]]: ←元组列表字符串可相互赋值
2. while <布尔表达式>:
<循环体> ←注意此处必须缩进!!
例1: 交互式循环: while moredata[0] == “y”: ←每次用户输入yes/no
例2: 哨兵循环:前导输入—while不是哨兵—处理数据—循环尾输入
哨兵举例: 累加成绩,可用 -1 作哨兵
计算实数,可用空字符串 “” (回车)作哨兵:
x = raw_input(“Input and Enter to quit:”)
while x != “” :
sum = sum + eval(x)
x = raw_input(“Input and Enter to quit:”)
例3: 后测试循环: 常用于输入合法性检查:
x = -1
while x<0 :
x = input ( “Please input a positive number: " )
例4: while 计数器循环: count = 0 → while count < n → count+=1
3. 循环的非正常中断: break跳出该循环体;continue中止本次循环
(应尽量避免使用,因为不符合结构化编程的基本思想)
4. 嵌套循环: 循环嵌套中,一个break只能跳出它所在的那一层循环
5. Python语言中的pass语句:什么都不做
3.5 结构化程序设计:
程序逻辑设计工具: 程序流程图 伪代码 层次图 结构图 等
基本内容: 只用三种基本控制结构(顺序、条件分支、循环)、
goto语句是有害的、单入口单出口的程序块第四章 模块化编程
4.2 Python语言中的函数:
1.没有返回值的函数(过程):
函数定义: def <函数名> (<形式参数>) : ←形式参数用逗号分隔
<函数体>
函数调用: <函数名> (<实际参数>) ←注意参数次序匹配
2.一般程序文件格式: tree.py
def treetop(ch):
xxx
xxx
def star_treetop():
treetop(”*")
xxx
def treetrunk():
xxx
def main():
star_treetop()
treetrunk()
main() ←最后一行调用主函数,启动整个程序
*3.关键字参数: 可以快速为特定参数忽略次序传递值,而其他参数采用默认值:
例: def f( a , b=7 , c=2 ) : ←为函数参数指定默认值
print a,b,c
有: f(2005)得2005 7 2 ; f(1927,8,1)得1927 8 1 ; f(1927,c=1)得1921 7 1
4.变量的作用域: 局部变量与全局变量
全局变量的用法: 在函数体中声明全局变量,即可对其操作: global x
def f() :
global x
x=x+1
print x
5.函数的返回值: return <表达式1> , … , <表达式n>
注意1:
如果函数有多个返回值,可以使用多变量同时赋值语句: a,b = xxx( yyy )
也可以使用一个变量来接收,因为返回值是一个元组: v = xxx( yyy )
注意2:
若函数直接用return结束(无返回值),或不含return语句
则会返回一个称为None的类型为NoneType的特殊对象,无法用于普遍计算
4.3 自顶向下设计,自底向上实现: 画结构图 (书P149)
顶层设计 (main函数) → 第二层设计 (各模块函数) → 第三+层设计 (进一步细化)
4.4 Python模块:
1.模块的创建和使用:任意文本编辑器输入Python语句,保存为.py文件即得模块
使用模块中对象必须用import或from语句导入模块,import导入后需加模块名前缀
2.Python程序架构: 通常为一个顶层主文件和多个模块文件(均为.py文件)
3.标准库模块: help()进入联机帮助: help> 例如可以输入模块名称获得相关信息
4.模块的有条件执行: Python区分 “用import导入模块” 和 “直接执行模块”:
若直接执行模块,则__name__值为’main’
若用import导入模块,则__name__值为模块名
区分的使用方法:在 main() 前加上一行 if name == ‘main’:
这样用import导入模块则不会调用main(),直接执行模块才会调用
示范: if name==‘main’:
main()
第五章 图形编程
5.1 概述:
对象:不仅存储了一些信息,而且还有对这些信息的操作。术语称为属性和方法。
5.2 Tkinter图形编程:
Tkinter是Python自带的标准模块,是一个功能强大的GUI工具包
IDLE本身就是Tkinter写的程序,所以在IDLE中执行Tkinter语句会有问题
故在交互式环境中演示Tkinter,可使用命令行环境执行
1.导入模块及创建根窗口:
from Tkinter import *
root = Tk() ←创建一个根窗口(实际为一个对象),在其中绘制图形
2.创建画布:
(1) c = Canvas( <窗口> , <选项1> = <值1> , <选项2> = <值2> , … )
常用选项包括height、width、bg(或background),不设置即为默认值
例:c = Canvas(root , width = 300 , height = 200 , bg = ‘white’) 单位为像素
(2) 创建画布对象后,布置画布: c.pack() ←pack()是一种布局管理器
在面向对象编程中,常用“点表示法”——“对象.操作”来对对象中的数据操作
(3) 坐标系: 左上角为原点,上边为+x,左边为+y,右下角为 (299,199)
除像素外也支持其他度量单位: “5c” 5厘米、“50m” 50毫米、“2i” 2英寸,等
(4) 图形项的标识: 标识号(Tkinter自动为图形项赋唯一整数编号)和标签
每个图形项可与0、1或多个标签相关联,而同一标签可与多个图形项相关联
三种为图形项指定标签的方法:
<1> 创建时用选项tags来指定单个字符串或字符串元组(多个名字)
r1 = c.create_rectangle( 20 , 20 , 100 , 80 , tags="#1" )
r2 = c.create_rectangle( 40 , 50 , 200 , 180 , tags=(“myRect”,"#2") )
<2> 创建后用画布的itemconfig()来设置
c.itemconfig( r1 , tags=(“myRect” , “rectOne”) )
<3> 利用画布的addtag_withtag()来为图形项增添新标签
c.addtag_withtag( “ourRect” , “rectOne” )←为rectOne加ourRect标签
(5) 画布对象的方法:gettags() find_withtag() delete() move()
c.gettags(r1) c.find_withtag(“myRect”) c.delete(r1) c.move(r2,10,20)
3.在画布上绘图:
(1) 矩形: id = create_rectangle( x0,y0,x1,y1,<选项设置>… ) ←返回标识号
(矩形不包含点(x1,y1),即该点在矩形右下角之外)
<选项设置>包含标签tags,以及:
轮廓线outline设置颜色,空串为不显示轮廓线(透明),默认为黑色
轮廓线宽度width,默认为1像素
矩形内部颜色fill,空串为内部透明,默认为空串
虚轮廓线dash取整数元组,常用二元组(a,b),a画b跳,a=b简记(a,)
矩形图形显示状态state,显示与不显示二者交替即形成闪烁效果:
默认NORMAL(或"normal"),设为HIDDEN(或"hidden")不显示
以上参数均可使用itemconfig()来设置
先画的图形在底部,后画的图形在顶部,堆叠则顶部图形会覆盖底部图形
Tkinter画布提供了对显示列表(堆叠次序)重新排序的方法
画布对象画“点”可以画一个1像素大小的矩形来当作点,比如(50,50,51,51)
坐标可以使用两个二元组或一个四元组表示,而且可以存入变量使用
(2) 椭圆形: id = create_oval( x0,y0,x1,y1,<选项设置>… ) ←用限定框定义
(圆形是椭圆形的特例,将限定框画为正方形即可)
(3) 弧形: id = create_arc( x0,y0,x1,y1,<选项设置>… )
<选项设置>包含通用选项,以及:
弧形开始位置用start,其值为一个角度,默认0°
弧形结束位置用extent,表示从开始位置逆时针旋转的角度,默认90°
选项style规定弧形式样,有三种可选值:
PIESLICE(或"pieslice")扇形,是默认值,可填充颜色
CHORD(或"chord")弓形,可填充颜色
ARC(或"arc")弧,不可填充颜色
(4) 线条: id = create_line( x0,y0,x1,y1,…,xn,yn,<选项设置>… )
(默认一条折线,可通过调整smooth选项为非0值,使其成为B样条曲线)
<选项设置>中:
没有outline,只有默认值为黑色的fill;width设置线条宽度
可将smooth调整为非0值,使图形整体为一条平滑B样条曲线
用arrow设置箭头,默认NONE,设置为BOTH(或"both")在两端,
设置为FIRST(或"first")在起点,设置为LAST(或"last")在终点
用arrowshape设置箭头形状,值为三元组(d1,d2,d3),为箭头三边长
(5) 多边形: id = create_polygon( x0,y0,x1,y1,…,<选项设置>… )
(依次折线连接各点,最后首尾相连)
<选项设置>中:
可将smooth设置为非0值,绘制平滑B样条曲线围成的图形
*多边形outline默认为空串,即轮廓线不可见,而fill默认为黑色
(6) 文本: id = create_text( x,y,<选项设置>… )
<选项设置>中包含:
text即为要显示的字符串,其中可以使用换行符"\n"
用anchor“锚点”指定哪个位置与坐标对齐,包括:
NW,N,NE,W,CENTER,E,SW,S,SE九个方位,默认CENTER
用fill设置文本颜色,默认黑色,设置为空串则文本不可见
用justify控制多行对齐方式,值为LEFT,CENTER,RIGHT,默认LEFT
用width控制文本宽度,超出宽度就要换行
程序中读取文本使用画布对象itemcget(),修改文本使用对象itemconfig()
例:print c.itemcget( id , “text” ) 以及 c.itemconfig( t1 , text=“NorthWest” )
(7) 图像: 针对gif图像,利用Tkinter提供的PhotoImage类来创建图像对象
用法: img = PhotoImage( file = <图像路径文件名> ) ←返回一个图像对象
id = c.create_image( x , y , image = <图像对象> , <选项设置>… )
其中选项设置可以使用anchor“锚点”,设置方法与文本相同
对其他常见的格式如jpg,可以用Python图像库(PIL)转换为Tkinter图像对象
4.图形的事件处理:
例:画布和图形对象的事件处理
from Tkinter import *
def canvasFunc(event):←event为事件类型,比如点鼠标左键,敲击键盘等
xxx
def textFunc(event): ←同上,均为事件处理函数
xxx
root = Tk()
c = Canvas( root , xxx , xxx )
c.pack()
t = c.create_text( xxx , xxx , text = “xxx” )
c.bind( “”,canvasFunc ) ←事件绑定,画布对象c与鼠标左击绑定
若发生此事件,则执行canvasFunc函数
c.tag_bind( t , “” , textFunc )←画布c上的图形项t与鼠标右击绑定
若发生此事件,则执行testFunc函数
root.mainloop() ←主事件循环,进入根窗口的mainloop监控发生事件并处理
5.3 编程案例:
1.字符串中的"%%“表示输出一个%符号,而不是字符串的格式化输出含义
2.事件处理函数如果需要引用main()中的参数,可以放在main()函数内部来定义
3.当鼠标指针移动到某个图形项上面时,即发生事件”"
4.动画修改图形位置后,必须执行一个更新画布显示的方法c.update()显示新画面
5.两个画面之间的停顿可以from time import sleep用sleep()函数控制停顿多少秒
5.4 软件的层次化设计: 图形库graphics (http://mcsp.wartburg.edu/zelle/python/)
1.图形库graphics是Tkinter绘图以面向对象方式的重新包装(需下载),方便使用
2.在from graphics import 后,win = GraphWin(“xxx”,300,200)创建绘图窗口界面
支持setBackground()改编窗口背景颜色,以及getMouse()暂停至鼠标点击
3.程序结束后通过win.close()即可关闭图形窗口
4.点: p = Point(100,80) 作点 p.draw(win) 画在win窗口 p.move(20,30) 移动
print p.getX(),p.getY() 显示点坐标 p.setFill(‘red’) 设置颜色
p.setOutline(‘blue’) 设置轮廓线颜色(对点,意义同上) p.clone() 复制
p.undraw() 隐藏(可通过draw()使其再显现)
5.直线: line = Line(<端点1>,<端点2>) 其中两个端点都是Point对象
其他用法同上,此外支持setArrow()画箭头(‘first’ ‘last’ ‘both’ ‘none’ 四种)
6.圆形: c = Circle(<圆心>,<半径>) 其中圆心是Point对象,半径是数值
其他用法同上,此外支持getRadius()方法,获取圆形对象的半径
7.椭圆: o = Oval(<左上角>,<右下角>) 其中两个角都是Point对象,指定外接矩框
8.矩形: r = Rectangle(<左上角>,<右下角>) 其中两个角都是Point对象
其他用法同上,此外支持getP1() getP2() getCenter(),均返回Point对象
9.多边形: poly = Polygon(<顶点1>,…,<顶点n>) 此外支持getPoints()得到顶点
10.文本: t = Text(<中心点>,<字符串>) 其中中心点是Point对象
其他用法同上,setText(<新字符串>)改变文本,另getText()和setTextColor()第六章 大量数据的表示和处理
6.2 有序的数据集合体:
Python序列都是以面向对象方式实现的,因此可以通过调用序列对象的方法实现:
s1+s2 sn或ns s[i] s[i:j] s[i:j:k] (间隔为k) les(s) min(s) max(s)
返回True或False: x in s 和 x not in s
比较运算: 序列s和t大小按字典序确定,先比较s[0]和t[0],相等再向后比较
1.字符串:
字符串对象的方法,与string库函数对应(注意字符串数据不可修改):
s.capitalize() s.center(width) s.count(sub) s.find(sub) s.ljust(width) s.lower()
s.lstrip() s.replace(old,new) s.rfind(sub) (最后一次出现) s.rjust(width)
s.rstrip() s.split(s) s.split(’,’) (依逗号分割) s.upper() s.islower() s.isupper()
2.列表:
列表长度不确定,而且是可以修改的:
a[i] = x a[i:j] = b (将片段改为列表b) del a[i] del a[i:j]
列表对象的方法:
l.append(x) (将x添加到列表尾部) l.reverse() (将次序颠倒)
l.sort() (使用默认比较函数cmp对列表排序) l.sort(mycmp)
l.index(x) (x在列表第一次出现处的索引) l.insert(i,x)
l.count(x) l.remove(x) (删除x在列表中的第一次出现)
l.pop() (删除列表中最后一个成员并返回该成员) l.pop(i)
3.元组:
多变量同时赋值实际为元组赋值;元组创建后不可修改;元组对象的方法:
t.index(x) (x在元组第一次出现处的索引) t.count(x)
元组类型的名字tuple可以用作函数,将字符串或列表转换成元组对象
6.3 无序的数据集合体:
1.集合:
两种创建方式:花括号 { } 与函数 set( 字符串、列表、元组等 )
注意:空集只能用 set() 创建,因为 { } 创建的是空字典
集合在创建时会自动删除重复的数据
集合运算: x in s(是否属于)s1|s2(并集)s1&s2(交集)s1-s2(差集)
s1^s2(对称差)s1<=s2(是否子集)s1
与序列相同,集合也可以用for遍历: for x in s: xxx
集合是可修改的数据类型,但是集合的元素只能是不可修改的类型,即:
集合的元素可以是数值、字符串、元组等,不能是列表、集合、字典等
不过集合的元素可以是不可修改集合类型frozenset,用frozenset()创建
集合对象的方法:k可以是列表、元组、集合等
s.union(k)(并集)s.intersection(k)(交集)
s.difference(k)(差集)s.symmetric_difference(k)(对称差)
s.issubset(k) s.issuperset(k) s.update(k)(即s1|=s2)
s.add(x) s.remove(x)(无x则出错)s.discard(x)(无x也不出错)
s.pop()(删除并返回任一元素)s.clear()(删除所有元素)s.copy()
2.字典:键值对 { key1:value1 , key2:value2 , … , keyn:valuen }
注意key须为不可修改类型数据(数值字符串元组等),value任意类型数据
还可用类型构造器dict()创建字典:关键字参数形式 或 序列(列或元)形式
d1=dict( name=“Lucy” , age=8 , hobby=(“bk”,“gm”) )
d2=dict( [ [(5,1),‘Worker’] , [(6,1),‘Child’] , [(7,1),‘CPC’] ] )
访问相应键的值: <字典>[<键>] ,若键为元组则元组括号可省略: d2[7,1]
字典型数据可修改: <字典>[<键>]=<新值> ,若该键不存在则添加新键值对
常用的创建字典方式即为:从空字典开始利用循环语句添加键值对
用 del <字典>[<键>] 删除字典条目;字典对象的方法:
d.clear()(删除所有条目)d.has_key(k) d.keys()(返所有键的列表)
d.values()(返所有值的列表)d.items()(返所有(k,v)元组的列表)
6.4 文件:
1.文件的基本概念:
目录路径:\与/均为分隔字符,但\可能会被Python解释为转义符,故用/或\
文件格式:文本文件(无格式字符串)与二进制文件(jpg、mp3、doc等)
2.文件操作: f = open(<文件名>,<打开方式>) ←方式分’r’(读)‘w’(写)‘a’(加)
注意:写方式打开文件时,若该文件不存在则创建,若该文件已存在则覆盖
读文件:<变量> = <文件对象>.read() ←读取当前位置至末尾并返回字符串
<变量> = <文件对象>.read(n) ←读取当前位置后n个字符并返字符串
<变量> = <文件对象>.readline()←读取当前位置至下个换行符并返串
<变量>=<文件对象>.readlines() ←读当前至末尾所有行并返行的列表
注意:当前读写位置变化;’\n’占一个字符;位于文件末尾则返回空串空列表
写文件:<文件对象>.write(<字符串>) <文件对象>.writelines(<字符串列表>)
注意:换行符需要人工添加;以’a’追加方式打开文件,当前位置定在末尾;
若以’a’追加方式打开的文件不存在,则同’w’一样,将创建一个新文件
关闭文件:<文件对象>.close() 释放资源;(写方式打开)存盘;应该及时
重新定位:<文件对象>.seek(0) 将当前位置定位为开头,不用重新打开文件
程序常见结构: while + readline() + 判断是否至末尾(空串),或:
for + readlines() 等同于Python允许的 for line in rifle: …
后者更节约内存,其中rfile为读方式打开的文件变量
3.编程案例:文本文件分析:
字符、单词、行的计数:
<字符数>=len(f.read())
<行数>=len(f.readlines())
<单词数>=len(string.split(f.read()))
每个单词出现次数的累计计数,用字典来完成(可用if,常用try-except):
try:
dict[w] = dict[w] + 1
except KeyError:
dict[w] = 1
将全部子母设置为小写(用于识别),将全部标点符号替换为空格(split)
4.缓冲:
设置缓冲区buffer=f.read(n)使程序在内存和CPU存在限制的情况下完成任务
5.二进制文件与随机存取:
二进制文件:打开方式相应为"rb"、“wb"和"ab”
随机存取: <文件对象>.seek(n) <文件对象>.seek(n,m) 其中n可取正负值
m=0时相对文件开始位置偏移n(同<文件对象>.seek(n))
m=1时相对文件当前位置偏移n
m=2时相对文件末尾位置偏移n
用f.tell()方法可以显示当前读写位置
6.5 几种高级数据结构:
1.链表:
链接以结点在列表中的位置索引实现,可以省略对结点物理位置的操作
结点:包含一个数据元组和一个链接地址link的列表 [(xxx,…,xxx),link]
整个链表是一个结点的列表,不关注结点物理位置,只处理各结点链接地址
有单链表、双链表、循环链表等,还可以设计各种非线性数据结构如树和图
(可选方法案例:单链表的插入功能)
首先添加一个用来标记链表末尾的成员,其索引地址为0,将link值设定为-1
然后用循环list.append()方法添加结点,操作两个索引参数将结点插入链表
2.堆栈:具有“后进先出”(LIFO)性质的数据结构;可用列表、链表等来实现
以push(x)和pop()两个操作进行处理;辅助操作isFull()和isEmpty()的判断等
用列表实现堆栈:列表尾作为堆栈顶,用stack.append()和stack.pop()实现
例:计算机中数学表达式的运算就是将中缀翻译为后缀,利用堆栈算得结果
3.队列:具有“先进先出”(FIFO)性质的数据结构
以enqueue(x)和dequeue两个操作进行处理;可用列表、链表等来实现第七章 面向对象思想与编程
7.1 数据与操作:两种观点:
1.面向过程观点:按照数据与操作分离的观点,以过程为中心展开程序设计
2.面向对象观点:将数据和对数据的操作融合,形成有静态信息和动态行为的对象
3.类是类型概念的发展:类是广义的数据类型,类的值就是对象,也称为类的实例
7.2 面向对象编程:
1.Python类定义:其中的方法定义都是函数定义,只不过必须有第一个形参:self
形式: class <类名>:
<方法定义1>:
…
<方法定义n>:
使用 init 方法定义数据: def init(self,n): self.name=n ←实例变量
后续方法定义中可直接使用上述数据:def whatName(self): print self.name
同样,方法定义中也可以直接使用 self.f() 来调用同一个类中的其他方法
注意:实例变量可以在任何方法中定义,也可以在任何时候赋值
特例:可以只包含数据( init )而不包含操作,即相当于C的struct类型
命名:习惯用开头大写字母为类命名,用骆驼式为类中方法和实例变量命名
2.对象的创建:
形式: <变量> = <类名>(<参数>) ←相当于隐含的调用__init__()
其中的<变量>即为 init 中self参数所传递,<参数>为除self外其他参数
3.对象方法的调用:
消息的形式: <对象>.<方法>(<实参>) ←除self外其他参数,因self隐含传递
4.面向对象编程的优势:本人认为优势的本质在于:
通过更有针对性的类定义,使编程所需的数据和方法之间的联系更加紧密,
并且提供的相联接口更加全面、高效、易用。
5.类与模块化:继续谈面向对象编程的优势:
同时类的模块化可以比过程的模块化包含更多的信息和更强的通用性。
一般把若干相关类存储在一个模块文件“类库”中,对应面向过程的“函数库”
6.对象的集合体: 类 + 集合体 = 任意复杂的数据 (使用第6章的数据类型和结构)
7.3 超类与子类(也称:基类和派生类):
1.继承:
形式: class <子类名>(<超类名>):
<特殊属性1>
…
<特殊属性n>
超类与子类一般置于同一模块中,否则需要 class <子>(<模块名>.<超>):
举例1: class Student(Person): ←定义超类的子类
def init(self,n,y,u): ←子类属性的初始化
Person.init(self,n,y) ←首先初始化超类属性
self.univ = u ←再初始化子类的特有属性
def getUniv(self): ←只定义子类的特有方法即可
return self.univ
举例2: class Teacher(Person): ←若不定义init则自动调用超类的init
def setNum(self,n): ←实例变量可以在任何方法中定义
self.snum = n ←也可以在任何时候赋值
def getNum(self):
return self.snum
2.覆写:
在子类中用同名重新定义超类的方法,即为覆写;子类对象执行新方法
不过可通过 超类.被覆写的方法名(子类对象,参数) 来执行超类中的老方法
3.多态性:
同名称的方法对不同子类对象控制着不同的行为:使同一操作具有不同形态
7.4 面向对象设计:中心任务是设计各种对象,以类为设计单位
一种基于词性分析的面向对象设计方法:
描述问题→找出候选对象→确定其数据属性→确定其行为属性→实现对象方法→迭代设计第八章 图形用户界面
8.2 GUI编程:
1.Tkinter模块的常用构件类:
Canvas画布
Frame框架 例:
f=Frame(root,width=300,height=400,bd=4,relief=“groove”)
f.pack() relief为3D风格(按钮构件同样适用)
使用:将其它构件的放置区域设置为 f 即可(而不设置为 root )
此外框架构件可用于分隔两个标签构件
Button按钮 例:quitButton=Button(root , text=“Quit” , command=root.quit)
通常command为程序员自己定义的函数对象(函数名称,不带括号)
Checkbutton复选框 例:Checkbutton(root , text=“A Choice”).pack()
查询和设置选项状态:关联IntVar类控制变量(值为0或1)
v = IntVar()
Checkbutton(root , text=“A Choice” , variable=v).pack()
然后可以通过 v.get() 和 v.set() 来查询和设置复选框状态
Entry录入框 可支持单行文本录入与编辑 例:Entry(root).pack()
获取用户输入:关联StringVar类控制变量
v = StringVar()
e = Entry(root , textvariable=v)
e.pack()
然后可以通过 v.get() 和 v.set() 来获取和设置录入框的内容
密码输入显示 * 号:show属性设置为 '’ 即可
Text文本区 可支持多行文本录入与编辑,用法与Entry类似,用途更多更复杂
Label标签 例:aLabel = Label(root , text=“hello”)
Listbox列表框
Menu菜单
使用add_command、add_cascade、add_checkbutton、add_radiobutton
添加菜单项:命令、级联式菜单、复选框、一组单选按钮
使用add_separator在菜单中添加分隔线
例: m=Menu(root)
root.config(menu=m) 将m置于窗口顶部形成菜单栏
filemenu=Menu(m)
m.add_cascade(label=“File” , menu=filemenu)
filemenu.add_command(label=“New” , command=xxx)
filemenu.add_separator() 不需要布局管理器来使之可见
filemenu.add_command(label=“Exit” , command=xxx)
mainloop()
Message消息
Radiobutton单选按钮 例:Radiobutton(root , text=“One”).pack()
若干单选按钮合并一组:关联同一个IntVar类(或StringVar类)控制变量
v = IntVar()
v.set(1) 设置初始默认选项
Radiobutton(root , text=“One” , variable=v , value=1).pack()
Radiobutton(root , text=“Two” , variable=v , value=2).pack()
Radiobutton(root , text=“Three” , variable=v , value=3).pack()
然后可以通过 v.get() 和 v.set() 来查询和设置复选框状态
Scrollbar滚动条
Toplevel顶层窗口 创建自由的新窗口,但是必须先创建好根窗口,之后才可
例: root = Tk()
Label(root , text=“hello”).pack()
top = Toplevel()
Label(top , text=“world”).pack()
2.需先建立每个程序有且只有一个的根窗口: root = Tkinter.Tk()
root.title(“xxx”) 修改标题 root.geometry("400400") 设置大小(宽*高)
3.进入 root.mainloop() 事件循环之后,若要将控制跳出mainloop,返回核心部分:
必须使用 root.quit() 方法来退出事件循环 (其它构件类型均可mainloop和quit)
4.大多数构件在创建后须经由布局管理器(Pack、Grid、Place)布置之后才可见
过于简单: Pack布局管理器用法:
.pack() 沿垂直方向打包 .pack(side=“left”) 沿水平方向打包
.pack_forget() 使构件不可见;从新布局才可见
使用较多: Grid布局管理器指定构件的行列坐标,默认坐标为下一行、第0列。用法:
.grid(row=1 , column=2)
.grid(sticky=W) 左对齐 .grid(sticky=E+W) 水平延伸
.grid(sticky=E+W+N+S) 或 .grid(sticky=NW+SE) 水平垂直延伸
.grid(columnspan=2 , rowspan=2) 设置构件的坐标跨度(多单元格)
.grid_forget() 使构件不可见;从新布局才可见
比较复杂: Place布局管理器指定构件在父构件中的位置坐标,通过锚点摆放。用法:
Label(root , text=“Hello”).place(x=199 , y=199 , anchor=SE)
(注意锚点指构件的相应位置)
还可以用相对坐标 (relx,rely) ,表示比例位置:
Label(root , text=“Hello”).place(relx=0.25, rely=0.5 , anchor=SW)
还可以指定构件的绝对尺寸和相对尺寸
.place_forget() 使构件不可见;从新布局才可见
5.每种构件类都提供config方法修改属性值(fg前景色、bg背景色、width宽度等)
6.输出属性值使用 aLabel[‘text’] ;修改单个属性也可以使用 aLabel[‘text’] = ‘HaHa’
7.构件的master和children属性记录父构件和子构件;aLabel.master可引用父构件
8.对话框:分为模态(必须先操作该对话框)和非模态(不影响其他窗口操作)
标准对话框:tkMessageBox模块、tkFileDialog模块、tkColorChooser模块
定制对话框:编写一个创立顶层窗口并布局的函数,即可调用而显示对话框
8.3 Tkinter事件驱动编程:
1.事件: <修饰符> - <类型符> - <细节符>
鼠标: 双击鼠标左键(123对应鼠标左中右键)
或简写为 <3> 按下鼠标右键
按下鼠标左键并移动鼠标
鼠标指针进入构件
鼠标指针离开构件
键盘: 或简写为 a 按下a键 *注意1为键盘,<1>为鼠标
按下空格键 按下小于号键
按下回车键
按下任意键
同时按下Shift和↑键 Alt、Ctrl组合同理
2.事件对象: Event对象,常用属性有:
x y 相对构件左上角坐标 x_root y_root 相对屏幕左上角坐标
num 鼠标键号 char ASCII字符键的字符,特殊键为空串
keysym ASCII字符键的字符,特殊键为该键名称字符串
keycode 所按键的编码,非键上字符的编码
keysym_num keysym的数值表示,ASCII字符键的字符ASCII码
3.事件处理:
3.1 绑定事件与事件处理程序: <构件实例>.bind(<事件描述符>,<事件处理程序>)
事件处理程序由用户自定义,不由应用程序调用而是由系统调用,称为回调函数
例: def callback(event):
print event.x, event.y 输出鼠标点击的坐标
然后创建根窗口、创建构件、构件.bind、构件.pack、root.mainloop
3.2 键盘事件需设置唯一焦点:
任何时刻只能一个构件占有焦点,键盘事件发送至此:.bind后,构件.focus_set()
3.3 绑定到多个事件,若具有“特殊与一般”关系,先调用关系最“近”的事件处理程序
3.4 Tkinter四种绑定层次:
实例绑定:f.bind("" , callback)
类绑定:绑定针对构件类,可用任何构件实例的bind_class()方法实现
例: root.bind_class(“Button” , “” , callback)
窗口绑定:对根窗口或顶层窗口绑定,则对其中所有构件有效 root.bind(…)
应用程序绑定:对应用程序中的所有构件有效,用任何构件实例bind_all()方法实现
例: root.bind_all(’’ , printHelp)
*事件的传递层次依上述次序,从微观到宏观;与绑定语句的次序无关
3.5 协议处理:处理来自操作系统窗口管理器的协议消息
例:接管关闭窗口的请求,使用:(窗口构件为根窗口或顶层窗口)
<窗口构件>.protocol(“WM_DELETE_WINDOW”) , <处理程序>)
另:若要主动关闭窗口,调用窗口的 destroy() 方法即可
3.6 虚拟事件:用户自定义新的事件类型,形式是 <<事件名称>>
对相应构件w使用: w.event_add("<>","<3>","") 右键或F1
再对其绑定事件: w.bind("<>" , myHandler) 即可
8.4 模型-视图设计方法:
1. 将整个GUI程序封装成一个类,在类中建立图形界面并处理各种交互事件
三种建立程序主窗口的方式:
直接在类的构造器init中创建根窗口;
在程序外创建根窗口,作为参数传递进类的构造器;
将应用程序类定义为框架构件类的子类,即程序就是窗口,窗口就是程序:
class MyApp(Frame):
def init(self):
Frame.init(self) 先用父类的构造器初始化
b=Button(self , …)
…
app = MyApp()
app.mainloop()
*另:通常为应用程序类专门定义启动方法 run() 用来启动程序功能(比如主循环)
2. 模型视图设计(MV方法): 视图(用户界面) ← 控制器 → 模型(核心逻辑)
*优势在于视图与模型可以分别独立地设计,即可以为同一模型设计多种视图
程序规格→明确候选对象→实现模型(模型类构造器init的参数留有界面对象传递接口)→明确界面需提供的方法→基于文本的用户界面→实现GUI(注意mainloop和quit运用)第九章 模拟与并发
9.1 模拟:
1.混沌现象(chaos):在确定性系统中发生的看上去随机、不规则的运动。
特征:不可预测性、初值敏感性
2.随机数生成函数(伪随机):
random 模块中的整型生成函数 randrange() 和浮点型生成函数 random() :
randrange(1,3) 随机生成1和2 randrange(2,100,2) 随机生成100以内偶数
random() 不需要提供参数,生成 [0,1) 区间中的浮点数
9.2 原型法(prototype):由核心到完备,由简易到完善
确认基本需求→创建原型→向用户演示或交付试用,获得反馈意见→改善原型,回上一步
9.3 并行计算:
1. 串行(serial):仅当一个程序执行完毕,下一个程序才能开始执行
并发(concurrent):一个处理器,多个相互独立的程序交叉执行
并行(parallel):多个处理器,多个程序分配到各处理器,同时执行
2. 进程(process):程序的一次执行所形成的实体(多次执行会生成多个进程)
线程(thread):程序中的一段代码,构成程序中一个相对独立的执行单位
因多线程同属一个程序,故多线程并发比多进程并发快得多,通信也更容易
3.线程是一种非确定性的计算模型:
多线程程序中的并发执行没有确定次序可言,同一程序的多次执行未必导致同样的结果
4.Python多线程编程: thread 模块和 threading 模块
thread模块用法:
(1) 创建一个新线程并立即返回其标识号:thread.start_new_thread(<函数>,<参数>)
<参数>为元组;新线程执行<函数>,调用<参数>作为实参传递,函数执行完则线程结束
(2) 中止主线程:thread.interrupt_main() 在主线程中触发KeyboardInterrupt异常
(3) 主线程结束,控制返回给Python解释器,屏幕显示Python解释器提示符">>>"
此时若子线程尚未结束,则子线程结束时无法返回,需要键盘按下Control+C中止子线程
threading模块用法:
(1) Thread类创建线程对象,并用start()方法来启动线程:
t=Thread(target=<函数>,args=<参数>) 然后 t.start()
(2) 自定义Thread类的子类,定制用户自己的线程对象 →
重定义__init__()方法,即定制自己的构造器添加参数(注意先执行基类构造器)→
重定义run()方法,即指定定制线程执行的代码(因start()方法会调用run()方法)→
用线程类创建线程实例,调用start()方法或直接调用run()方法启动新线程执行任务
(3) 线程.isAlive()方法可以检查线程正在运行还是已经结束(即run()方法执行结束):
可以通过该方法,配合全局变量(公共数据)的使用,进行协同,即:
控制多个线程之间的协作和同步,来完成工作任务(*意义重大)
(4) 可以通过.getName()和.setName()方法读出或设置线程名称
(5) 一个线程可以调用另一个线程的join()方法:
将导致原线程暂停执行,等候另一线程(至某一时间),达到同步的目的第十章 算法设计和分析
10.1 枚举法:
线性搜索:对数据集合从头到尾进行扫描,扫描过程中检验每一个成员
(利用n层嵌套循环即可枚举n维搜索空间中的数据)
综上:枚举策略设计算法的一般步骤:
1.确定枚举对象、枚举范围和判定条件
2.枚举各可能解,逐一验证是否所需的问题解
3.尽量减小枚举范围,提高算法效率
10.2 递归:
在一个函数的定义中直接或间接地用到该函数自身,这种函数称为递归函数。
(编程语言中的递归层数是有限制的,当突破限制时递归过程会终止)
二分搜索:有序数据集的快速搜索算法,只需不断调整搜索范围(上下界)即可
*许多问题既可用循环(迭代)实现,也可用递归实现(递归更耗费内存和时间)
但对于有些问题,递归算法具有极大的优势,比如汉诺塔问题:
使用递归关键在于,找准算法中只是问题规模变低了的重复任务,及它的奠基情形
(递归函数能够直接计算结果的情形,称为奠基情形,很重要需多加考虑和测试)
综上:递归定义必须满足以下条件才是良定义的:
1.有一个或多个无需递归的奠基情形
2.递归总是针对规模更小的问题
10.3 分治法:
将难以处理的较大问题分解为若干个较小的子问题,然就分别解决这些子问题,并从子问题的解构造出原问题的解。
选择排序:每次找到剩余数据中的最值放在这些数据最前端,然后忽略此数并继续
(选择排序算法当数据量很大时,性能很差)
归并排序:每次二分数据集、局部排序、全局归并,并重复以上三步进行递归操作
综上:由于子问题通常与大问题本质上是相同的,故可以用递归方法来设计算法,所以分治法常常与递归法结合起来使用。
10.4 贪心法:
在求解过程每一步都尽量作出当前情况下局部最优选择,以期最终得到全局最优解
例:Prim算法和Kruskal算法求解图论中的“最小支撑树”问题,可以得到全局最优解
Prim算法:不断从已通达点和未通达点间选择最短的那条路径,直至所有点都可通达
Kruskal算法:不断选择尚未选择的路径中最短的那条路径,若两端已通达则忽略此路径
综上: 贪心算法模式:
输入:一个候选对象集合
输出:由某些候选对象组成的全局解
重复以下步骤,直至得到全局解:
从候选对象中选择当前最优者,并加入到局部解中
10.5 算法分析:
算法复杂度:时间复杂度(CPU)和空间复杂度(存储器)
因为现代计算机存储空间越来越大,所以只关注时间复杂度,即算法的操作数
算法复杂度的大O表示法:问题规模为n,用n的函数的同阶无穷大量来表示复杂度
O(n) - 线性时间算法(线性搜索) O(logn) - 对数时间算法(二分搜索)
O(n2) - 二次方时间算法(选择排序) O(nlogn) - nlogn时间算法(归并排序)
O(2n) - 指数时间算法(汉诺塔递归)
10.6 不可计算的问题:
汉诺塔问题称为难解问题,因它的递归解法是指数时间算法;但还存在不可解问题
Turing-Church论题:一个问题是算法可计算的,当且仅当该问题能用图灵机计算
停机问题:一台图灵机能否判断其它任意一台图灵机是否终止?反证法证明了不能第十一章 计算+X
11.1 计算数学:
误差:截断误差(以有限代替无限过程中产生)舍入误差(计算机的数表示的限制)
舍入误差的控制:
加减法注意控制加减次序,避免“大数吃小数”现象
减法和除法可以使用转换成等价计算公式的办法,避免舍入误差增大
通常用浮点乘除运算(FLOPS)的次数来度量算法效率,称为算法的计算量
(相对乘除运算,加减运算所耗时间可以忽略不计)
控制计算量很重要,例如克莱姆法则解线性方程组不可行,但高斯消去法却很高效
病态与良态问题:问题对初值的敏感度,过于敏感称病态问题,反之称为良态问题
数值方法主要研究良态问题的数值解法,病态问题应当使用专门方法,或转为良态
数值稳定性(数值计算的算法须使数值稳定):
数值方法若在计算过程中能将舍入误差控制一定范围内,称数值稳定,否则称数值不稳定
数值计算方法与纯数学方法不同,其构造和算法实现必须考虑计算机的能力和限制
11.2 生物信息学:计算生物学的一个分支
11.3 计算物理学:即计算机科学+计算数学+物理学
计算物理与理论物理、实验物理一起构成了物理学的三大支柱
11.4 计算化学:即计算机科学+化学
11.5 计算经济学:即计算机科学+经济和管理科学