note:本文所有代码在python3环境下执行,可用jupyter notebook
2006年,美国CMU计算机系主任周以真提出
计算思维(Computational Thinking)
运用计算机科学基础概念求解问题、设计系统和理解人
类行为
1.生活中计算思维的应用
我们拥有:四个灶,锅碗瓢盆,食物原料。
我们完成:肉菜、素菜、甜点。
考虑因素:好吃、不能凉、搭配素菜
计算思维的人:有限资源、设定并行流程、得出最好效果
计算思维的应用IPO
输入I: 四个灶,一定数量的锅碗瓢盆,食物原料。
处理P: 做饭过程统筹设计
输出O: 肉菜,素菜,甜点。
计算思维的本质
抽象(Abstraction),自动化(Automation)
实证思维,逻辑思维,计算思维
随计算机科学发展而提出
理解计算机特征
将计算特征抽象为计算问题
程序设计实现问题的自动求解
计算机模拟解决问题
模拟现实世界计算过程提供一般情况下无法获得的信息
简单的模拟可以揭示某些困难问题的本质规律。
天气预报
飞行器设计
电影特效
核试验模拟
示例:体育竞技分析
基本规则
两个球员,交替用球拍击球
发球权,回合
未能进行一次击打回合结束
首先达到15分赢的比赛
该问题的IPO模式
输入I: 两个球员(A和B)的能力值,模拟比赛的 场次
处理P: 模拟比赛过程
输出O: 球员A和B分别赢得球赛的概率
一个期望的输出结果
模拟比赛数量:500
球员A获胜场次:268(53.6%)
球员B获胜场次:232(46.4%)
顶层设计(第一阶段)举例
步骤1:答应程序的介绍性信息
printIntro()
步骤2: 获得程序运行所需的参数:ProA, ProB, n,
probA,probB, n = getInputs()
步骤3: 模拟n次比赛
winsA, winsB = simNGames(n, probA, prpbB)
步骤4: 输出球员A和B获胜比赛的次数和概率
printSummary(winsA,sinsB)
代码:
def main():
printIntro()
probA,probB,n = getInputs()
winsA, winsB = simNGames(n,probA,probB)
printSummary(winsA,winsB)
# printIntro()函数;
def printIntro():
print('This programe simulates a game between two')
print('There ara two player, A and B')
print('Probability(a number between 0 and 1)is used')
# getInputs()函数;
def getInputs():
a = eval(input('What is the prob.player A wins?'))
b = eval(input('What is the prob.player B wins?'))
n = eval(input("How many games to simulate?"))
return a, b, n
# simNGames()函数(核心)
def simNGames(n,probA,probB):
winsA = 0
winsB = 0
for i in range(n):
scoreA,scoreB = simOneGame(probA,probB)
if scoreA > scoreB:
winsA = winsA + 1
else:
winsB = winsB + 1
return winsA, winsB
# simOneGame()函数;
def simOneGame(probA,probB):
scoreA = 0
scoreB = 0
serving = 'A'
while :
# simOneGame()函数完整代码:
def simOneGame(probA,probB):
scoreA = 0
scoreB = 0
serving = 'A'
while not gameOver(scoreA,scoreB):
if serving == 'A':
if random() < probA:
scoreA = scoreA + 1
else:
serving = 'B'
else:
if random() < probB:
scoreB = scoreB + 1
else:
serving = 'A'
return scoreA,scoreB
# gameOver()函数:
def gameOver(a,b):
return a==15 or b==15
# printSummary()函数
def printSummary():
n = winsA + winsB
print('\nGames simulated:%d'%n)
print('WinsforA:{0}({1:0.1%})'.format(winsA,winsA/n))
print('Wins for B:{0}({1:0.1%})'.format(winsB,winsB/n))
设计过程总结
自顶向下设计:
步骤1:将算法表达为一系列小问题;
步骤2:为每个小问题设计接口;
步骤3:通过将算法表达为接口关联的多个小问题来细化算法;
步骤4:为每个小问题重复上述过程。
自顶向下的设计
从顶层开始分解问题为更小的问题进行求解
自底想上的执行
从底层模块开始一个一个进行测试
程序写好后,需要通过运行程序进行测试
单元测试
小规模程序
直接运行
中等规模
从就结构图底层开始,逐步上升
先运行每个基本函数,在测试整体函数
较大规模
高级软件测试方法
体育竞技分析框架
#matchSim.py
from randon import random
def main():
def printIntro():
def getInput():
def simNGames():
def simOneGame():
def gameOver():
if __name__ == '__main__':
main()
体育竞技分析的例子
gameOver单元测试
(1):>>>importmatchSim
>>>matchSim.gameOver(0,0)
False
(2): >>>matchSim.gameOver(5,10)
False
(3): >>>matchSim.gameOver(15,3)
True
(4): >>>matchSim.gameOver(3,15)
True
simOneGame单元测试
(1) >>>importmatchSim
>>>matchSim.simOneGame(.5,.5)
(13,15)
(2) >>>matchSim.simOneGame(.5,.5)
(15,11)
(3) >>>matchSim.simOneGame(.3,.3)
(15,11)
(4) >>>matchSim.simOneGame(.3,.3)
(11,15)
模拟结果:
>>>matchSim.py
What is the prob.player A wins? .65
What is the prob.player B wins? .6
How many games to simulate? 5000
Games simulated:5000
wins for A:3360(67.2%)
wins for B:1640(32.8%)
软件
能够完成预定功能和性能的可执行的计算机程序,支持程序正常运行的数据,以及描述程序的操作和使用的文档
软件工程
将系统的,严格约束的,可量化的方法应用于软件的开发,运行和维护。
将工程化应用于软件
软件开发生命周期
确定问题
可行性分析
系统分析
编码
测试
安装,维护
瀑布模式
螺旋模式
快速原型模式
喷泉模式
混合模式
敏捷开发模式
瀑布模式
重视各个阶段的顺序性
当一个阶段的文档获得认可才进入下一阶段。
螺旋模式
设计,执行并测试原型
再设计,执行并测试新特征
将原型逐步扩展为最终程序
体育竞技分析
本质:模拟一场比赛simOneGame()
原型
假设每个球员都有机会在50对50的概率下赢得有一分
打了30 会合
谁会得分或改变球权
原型的例子
import random
def simOneGame():
scoreA = 0
scoreB = 0
serving = 'A'
for i in range(30):
if serving == 'A':
if random.random() < 0.5:
scoreA = scoreA + 1
else:
serving = 'B'
else:
if random.random() < 0.5:
scoreB = scoreB + 1
else:
serving = 'A'
print(scoreA)
print(scoreB)
simOneGame()
阶段一:构建最初的原型
阶段二:添加两个参数代表两个球员赢球的概率
阶段三:进行比赛,直到一个球员达到15分
阶段四:将比赛扩展为多场比赛
阶段五:建立完整的程序
以人为核心,迭代。循环渐进
针对传统的瀑布开发模式的弊端
分为多个相互联系,独立运行的小项目
软件一直处于可使用状态
敏捷开发更符合软件开发的规律
软件—植物
自底向下
逐步有序
遵循软件客观规律
迭代增量开发
开发效率更高
在传统方式中
管理者“控制”团队
团队成员被动的等待指令,独立工作,协作少
在敏捷开发方式中
管理者“激发”团队
团队成员共同参与
轻量级软件开发方法
Scrum
极限编程(XP)
精益开发(Lean Development)
动态系统开发方法(DSDM)
特征驱动开发(Feature Driver develpment)
水晶开发(Cristal Clear)
Scrum
一个开发过程
几种角色
产品负责人
流程管理员
开发团队
一套规范的实施方法
敏捷开发典型过程
1.对成品形成共识
2.建立和维护产品需求列表,并进行优先级排序
3.筛选高优先级需求进入本轮迭代开发
4.细节本轮迭代需求,一次在本轮迭代完成
5.每日召开站立会议
任务看板
6.对每轮迭代交付的可工作软件,进行现验场收和反馈
7.从第3步开始,开始下一轮迭代
以程序过程为设计流程的思想
程序设计中最自然的一种设计方法
结构化编程
举例
新闻报道:2015年8月22日,世界竞标赛揭开战幕,德国选手施瓦尼茨以20米37夺冠,中国选手巩立娇以20米30获得亚军。
评论员评论分析:
巩立娇是技术优秀型选手
施瓦尼茨是力量型选手
铅球飞行计算问题
在给定不同的投掷角度和初始速度下,求解计算铅球的飞行距离
IPO描述为:
输入:铅球发射角度、初始速度(m/s)、初始高度(m)
处理:模拟铅球飞行,时刻更新铅球在飞行中的位置
输出:铅球飞行距离(m)
简化问题
忽略空气的阻力
中立加速度9.8m/s^2
铅球飞行过程
铅球高度
飞行距离
时刻更新铅球在飞行中色位置
假设起始位置是点(0,0)
垂直方向运动距离(y轴)
水平方向上移动距离(x轴)
设计参数
仿真参数:投掷角度angle,ch初始速度velocity, 初始高度height,飞行距离interval
位置参数:x轴坐标xpos, y轴坐标ypos
速度分量: x轴方向上速度xvel,y轴方向上速度yvel
根据提示输入仿真参数
angle = eval(input("Enter the launch angle (in degrees):"))
vel = eval(input("Enter the initial velocity (in meters/sec):"))
h0 = eval(input("Enter the initial height (in meters):"))
time = eval(input("Enter the time interval:"))
计算初始速度:
x轴的速度
xvel = velocity*cos(theta)
y轴速度
yvel =
velocity*sin(theta)
from math import pi,sin,cos,radians
xpos = 0
ypos = 0
theta = radians(angle)
xvel = vel * cos(theta)
yvel = vel * sin(theta)
程序主循环
while ypos >= 0:
xpos = xpos + time * xvel
yvell = yvel - time * 9.8
ypos = ypos + time * (yvel + yvell) / 2.0
yvel = yvell
完整代码
from math import pi,sin,cos,radians
def main():
angle = eval(input("Enter the launch angle (in degrees):"))
vel = eval(input("Enter the initial velocity (in meters/sec):"))
h0 = eval(input("Enter the initial height (in meters):"))
time = eval(input("Enter the time interval:"))
xpos = 0
ypos = h0
theta = radians(angle)
xvel = vel * cos(theta)
yvel = vel * sin(theta)
while ypos >= 0:
xpos = xpos + time * xvel
yvell = yvel - time * 9.8
ypos = ypos + time * (yvel + yvell) / 2.0
yvel = yvell
print("\nDistance traveled:{0:0.1f}meters.".format(xpos))
程序模块化:
def getInputs():
angle = eval(input("Enter the launch angle (in degrees):"))
vel = eval(input("Enter the initial velocity (in meters/sec):"))
h0 = eval(input("Enter the initial height (in meters):"))
time = eval(input("Enter the time interval:"))
return angle,vel,h0,time
def getXYComponents(vel,angle):
theta = radians(angle)
xvel = vel * cos(theta)
yvel = vel * sin(theta)
return xvel, yvel
def updatPosition(time, xpos,ypos,xvel,yvel):
xpos = xpos + time * xvel
yvell = yvel - time * 9.8
ypos = ypos + time * (yvel + yvell) / 2.0
yvel = yvell
return xpos, ypos,yvel
def main():
angle,vel,h0,time = getInputs()
xpos.ypos = 0,h0
xvel,yvel = getXYComponents(vel,angle)
while ypos >= 0:
xpos, ypos, yvel = updatPosition(time,xpos,ypos,xvel,yvel)
print("\nDistance traveled:{0:0.1f}meters.".format(xpos))
面向过程程序设计基本步骤:
分析程序从输入到输出的各步骤
按照执行过程从前到后编写程序
将高耦合部分封装成模块或函数
输入参数,按照程序执行过程调试
总结面向过程程序设计特点
通过分步骤、模块化
将一个大问题分解成小问题
将一个全局过程分解为一系列局部过程
面向过程
最为自然、也是最贴近程序执行过程的程序设计思想
在面向对象的程序设计中也会使用面向过程的设计方法
真实世界的对象
特征:状态和行为
比如:猫
状态:名字,颜色,品种
行为:喵叫,摇尾巴,捉老鼠
真实世界对象
这个对象有怎样的状态
这个对象具有什么行为
台灯
状态:开,关
行为:打开,关闭
台式收音机
状态:开,关,当前音量,当前频道
行为:打开,关闭,增加音量,减少音量,搜索,扫描和调音
类:某种类型集合的描述,举例:人
属性
类本身的一些特征,如名字,身高和体重等属性
方法:
类所能实现的行为,如吃饭,走路和睡觉等方法
类定义解析:
class ClassName:
block
注意类名后面有个冒号,并且类名第一个字母要大写,在block块里面就可以定义属性和方法了。当一个类定义完之后,就产生了一个类对象。类对象支持两种操作:引用和实例化。引用操作是通过类对象去调用类中的属性或者方法,而实例化是产生出一个类对象的实例,称作实例对象。比如定义了一个people类:
class People:
#定义一个属性
name = 'jack'
#定义一个方法
def printName(self):
print(self.name)
1.People类定义完成之后就产生了一个全局的类对象,可以通过类对象来访问类中的属性和方法了。
2.当通过People.name(至于为什么可以直接这样访问属性后面再解释,这里只要理解类对象这个概念就行了)来访问时,people.name中的people称为类对象,这点和C++中的有所不同。
3.当然还可以进行实例化操作,方法为p=People(),这样就产生了一个People的实例对象,此时也可以通过实例对象p来访问属性或者方法了(p.name).
1.在上面代码中注释的很清楚了,name是一个属性,printName()是一个方法,与某个对象进行绑定的函数称作为方法。
2.一般在类里面定义的函数与类对象或者实例对象绑定了,所以称作为方法;而在类外定义的函数一般没有同对象进行绑定,就称为函数。
在Python中有一些内置的方法,这些方法命名都有比较特殊的地方(其方法名以2个下划线开始然后以2个下划线结束)。类中最常用的就是构造方法和析构方法。
class classname[(父类]名)]:[成员函数及成员变量]
__init__构造函数:初始化对象的各属性
在生成对象时调用,可以用来进行一些初始化操作,不需要显示去调用,系统会默认去执行。
构造方法支持重载,如果用户自己没有重新定义构造方法,系统就自动执行默认的构造方法。
__del__析构函数:销毁对象
在释放对象时调用,支持重载,可以在里面进行一些释放资源的操作,不需要显示调用。
内置方法 | 说明 |
---|---|
init(self,…) | 初始化对象,在创建新对象时调用 |
del(self) | 释放对象,在对象被删除之前调用 |
new(cls,*args,**kwd) | 实例的生成操作 |
str(self) | 在使用print语句时被调用 |
getitem(self,key) | 获取序列的索引key对应的值,等价于seq[key] |
len(self) | 在调用内联函数len()时被调用 |
cmp(stc,dst) | 比较两个对象src和dst |
getattr(s,name) | 获取属性的值 |
setattr(s,name,value) | 设置属性的值 |
delattr(s,name) | 删除name属性 |
getattribute() | getattribute()功能与getattr()类似 |
gt(self,other) | 判断self对象是否大于other对象 |
lt(slef,other) | 判断self对象是否小于other对象 |
ge(slef,other) | 判断self对象是否大于或者等于other对象 |
le(slef,other) | 判断self对象是否小于或者等于other对象 |
eq(slef,other) | 判断self对象是否等于other对象 |
call(self,*args) | 把实例对象作为函数调用 |
__init__():__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化
注意,这个名称的开始和结尾都是双下划线。
代码例子1:
# Filename: class_init.py
class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print('Hello, my name is', self.name)
#实例化
p = Person('init')
#调用方法
p.sayHi()
输出: Hello, my name is init
代码例子2:
class Movie(object):
def __init__(self,name,length):
self.name = name
self.length = length
def print_name(self):
print '电影的名称是:%s' % (self.name,)
实例执行情况:
>>> movie = Movie('大圣归来',90)
大圣归来
>>> print(movie.length
90
class Movie(object): 是固定写法,class表明正在声明的是一个类,Movie是类名,(object)表示继承自那些父类,就像java一样,所有的类都是继承自object类的,所以如果你写的不继承自其他的类就将其写成继承自object,当继承自object,括号连同括号里的object都可以省略写成:class Movie:
第一个方法init是构造方法,构造方法的第一个参数永远是self,表示这个类的对象本身,真正构造对象时,self这个参数不用写,python编译器会自己加上去,构造方法的作用就是对self对象进行赋值,如上面的将电影的名字和长度赋给self。
对于上面的Movie类我们可以用下面的代码来调用它:
#不继承任何类,就继承object类
class Movie:
def __init__(self,name,length):
self.name = name
self.length = length
def print_name(self):
print ('电影的名称是:%s' % (self.name,))
有了init方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
>>> movie = Movie('大圣归来',90)
大圣归来
>>> print(movie.length
90
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
__new__():__new__()在__init__()之前被调用,用于生成实例对象。利用这个方法和类属性的特性可以实现设计模式中的单例模式。单例模式是指创建唯一对象吗,单例模式设计的类只能实例化一个对象。
代码如下:
class Singleton(object):
__instance = None # 定义实例
def __init__(self):
pass
def __new__(cls, *args, **kwd): # 在__init__之前调用
if Singleton.__instance is None: # 生成唯一实例
Singleton.__instance = object.__new__(cls, *args, **kwd)
return Singleton.__instance
__getattr__()、__setattr__()和__getattribute__():当读取对象的某个属性时,python会自动调用__getattr__()方法。
例如,fruit.color将转换为fruit.__getattr__(color)。当使用赋值语句对属性进行设置时,python会自动调用__setattr__()方法。
__getattribute__()的功能与__getattr__()类似,用于获取属性的值。但是__getattribute__()能提供更好的控制,代码更健壮。
注意,python中并不存在__setattribute__()方法。
代码例子:
class Fruit(object):
def __init__(self, color="red", price=0):
self.__color = color
self.__price = price
def __getattribute__(self, item): # 获取属性的方法
return object.__getattribute__(self, item)
def __setattr__(self, key, value):
self.__dict__[key] = value
if __name__ == "__main__":
fruit = Fruit("blue", 10)
print fruit.__dict__.get("_Fruit__color") # 获取color属性
fruit.__dict__["_Fruit__price"] = 5
print (fruit.__dict__.get("_Fruit__price") ) # 获取price属性
#输出
blue
5
Python不允许实例化的类访问私有数据,但你可以使用object._className__attrName访问这些私有属性。
__getitem__():如果类把某个属性定义为序列,可以使用__getitem__()输出序列属性中的某个元素.假设水果店中销售多钟水果,可以通过__getitem__()方法获取水果店中的没种水果。
- 代码例子:
class FruitShop:
def __getitem__(self, i): # 获取水果店的水果
return self.fruits[i]
if __name__ == "__main__":
shop = FruitShop()
shop.fruits = ["apple", "banana"]
print shop[1]
for item in shop: # 输出水果店的水果
print(item),
#输出
banana
apple
banana
__str__():__str__()用于表示对象代表的含义,返回一个字符串.实现了__str__()方法后,可以直接使用print语句输出对象,也可以通过函数str()触发__str__()的执行。这样就把对象和字符串关联起来,便于某些程序的实现,可以用这个字符串来表示某个类。
- 代码例子:
class Fruit:
'''''Fruit类''' #为Fruit类定义了文档字符串
def __str__(self): # 定义对象的字符串表示
return self.__doc__
if __name__ == "__main__":
fruit = Fruit()
print (str(fruit) ) # 调用内置函数str()触发__str__()方法,输出结果为:Fruit类
print( fruit ) #直接输出对象fruit,返回__str__()方法的值,输出结果为:Fruit类
#输出
''Fruit类
__call__():在类中实现__call__()方法,可以在对象创建时直接返回__call__()的内容。使用该方法可以模拟静态方法。
- 代码例子:
class Fruit:
class Growth: # 内部类
def __call__(self):
print ("grow ...")
grow = Growth() # 调用Growth(),此时将类Growth作为函数返回,即为外部类Fruit定义方法grow(),grow()将执行__call__()内的代码
if __name__ == '__main__':
fruit = Fruit()
fruit.grow() # 输出结果:grow ...
Fruit.grow() # 输出结果:grow ...
#输出
grow ...
grow ...
学生课程评估:学分和平均绩点GPA
绩点计算以GPA 4分为准则
一门课程3学分
同学得了 “A”
3*4=12量分数
GPA计算以4分为准则,学生的成绩是“A”(即4点),“B”(即3点),“C”(即2点),“D”(即1点),“E”(即0点),GPA的计算如下:
在申请入学时,很多学校有GPA规定。GPA的计算为:
1.每个科目点数乘以学分。比如微积分是4分的课,成绩是A(即4点),微积分一科可以得16点
2.如果一学期修了4门课,英文3学分(成绩4点),历史3学分(成绩2点)物理3学分(成绩3点),加上微积分。
GPA是16+4x3+3x2+3x3 = 43,43/13 = 3.31
记录学生成绩文件students.txt.编写程序,通过读取文件找出平均绩点最高的学生,然后输出他的名字,学分和平均绩点。
定义Student类
#class Student:
def __init__(self,name,hours, qpoints):
self.name = name
self.hours = float(hours)
self.qpoints = float(qpoints)
def getName(self):
return self.name
def getHouse(self):
return self.hours
def getQPoints(self):
return self.qpoints
def gpa(self):
return self.gpoints/self.hours
GPU算法描述为
获取文件名
打开文件
设置第一个学生为best
对文件中的每一个学生
if s.gpa()>best.gpa()
设置s为best
打印best学生的信息
代码执行:
#定义Student类
class Student:
#初始化
def __init__(self,name,hours, qpoints):
self.name = name
self.hours = float(hours)
self.qpoints = float(qpoints)
#方法1:返回名字
def getName(self):
return self.name
#方法2:返回分数
def getHouse(self):
return self.hours
#方法3:返回
def getQPoints(self):
return self.qpoints
def gpa(self):
return self.qpoints/self.hours
def makeStudent(infoString):
name,scores,qpoints=infoString.split()
return Student(name,scores,qpoints)
# 主函数
def main():
#打开输入文件
fileName=input('Enter name of the grade file:')
f=open(fileName,'r')
#设置文件中第一个学生为best
best= makeStudent(f.readline())
#处理文件中剩余的行数据
for line in f:
#将文件中的每一行数据转换为一个学生对象
s= makeStudent(line)
#如果该学生是目前GPA最高的,则记录下来
if s.gpa()>best.gpa():
best=s
f.close()
#打印GPA成绩最高的学生信息
print('The best student is:',best.getName())
print('scores:',best.getHouse())
print('GPA:',best.gpa())
if __name__ =="__main__":
main()
# 执行结果:
Enter name of the grade file:C:\Users\Administrator\Desktop\exercise\python_learning\面向对象\students.txt
The best student is: 李四
scores: 100.0
GPA: 4.0
铅球对象属性
xpos
ypos
xvel
Yvel
构建投射体类Projectile
创建和更新对象的变量
主函数
def main():
angle,vel,h0,time = getInputs()
shot = Projectile(angle,vel,h0)
while shot.getY() >=0:
shot.updata(time)
print("\nDistance traveled:{0:0.1f}meters.".format(shot.getX()))
from math import sin,cos,radians
class Projectile:
def __init__(self, angle, velocity, height):
#根据给定的发射角度,初始速度和位置创建一个投射体对象
self.xpos = 0.0
self.ypos = height
theta = radians(angle)
self.xvel = velocity * cos(theat)
self.yvel = velocity * sin(theta)
def getXYComponents(vel,angle):
theta = radians(angle)
xvel = vel * cos(theta)
yvel = vel * sin(theta)
return xvel, yvel
def updatPosition(time, xpos,ypos,xvel,yvel):
xpos = xpos + time * xvel
yvell = yvel - time * 9.8
ypos = ypos + time * (yvel + yvell) / 2.0
yvel = yvell
return xpos, ypos,yvel
引入对象,程序模块化
from Projectile import *
def getInputs():
angle = eval(input("Enter the launch angle (in degrees):"))
vel = eval(input("Enter the initial velocity (in meters/sec):"))
h0 = eval(input("Enter the initial height (in meters):"))
time = eval(input("Enter the time interval:"))
return angle,vel,h0,time
def main():
angle,vel,h0,time = getInputs()
shot = Projectile(angle,vel,h0)
while ypos >= 0:
xpos, ypos, yvel = updatPosition(time,xpos,ypos,xvel,yvel)
print("\nDistance traveled:{0:0.1f}meters.".format(xpos))
所用代码在jupyter notebook中:
from math import sin,cos,radians
class Projectile:
def __init__(self,angle,velocity,height):
#根据给定的发射角度,初始速度和位置创建一个投射体对象
self.xpos=0.0
self.ypos=height
theta=radians(angle)
self.xvel=velocity*cos(theta)
self.yvel=velocity*sin(theta)
def update(self,time):
#更新投射体的状态
self.xpos=self.xpos+time*self.xvel
yvel1=self.yvel-9.8*time
self.ypos=self.ypos+time*(self.yvel+yvel1)/2.0
self.yvel=yvel1
def getY(self):
#返回投射体的y轴坐标
return self.ypos
def getX(self):
#返回投射体的x轴坐标(即水平距离)
return self.xpos
def getInputs():
a=eval(input('Enter the launch angle(in degrees):'))
v=eval(input('Enter the initial velocity(in meters/sec):'))
h=eval(input('Enter the initial height(in meters):'))
t=eval(input('Enter the time interval:'))
return a,v,h,t
def main():
angle,vel,h0,time=getInputs()
shot=Projectile(angle,vel,h0)
while shot.getY()>=0:
shot.update(time)
print('\nDistance traveled:{0:0.1f} meters.'.format(shot.getX()))
输出结果:
Enter the launch angle(in degrees):41
Enter the initial velocity(in meters/sec):14
Enter the initial height(in meters):1.8
Enter the time interval:0.8
Distance traveled:25.4 meters.
封装
从业务逻辑中抽象对象时,赋予对象相关数据与操作,把一些数据和操作打包在一起的过程就是封装
对象的实现和使用是独立的
支持代码复用
举例:封装
Projectile将投射体属性和方法封装在类的内部
不必关心铅球内部如何实现
Projectile类可以被多个程序,多个对象所使用
多态
对象怎么回应一个依赖于对象类型或种类的消息
在不同情况下用一个函数启用不同的方法
灵活性
举例:多态
能够直接说明多态的两段示例代码如下:
1、方法多态
_metaclass_=type # 确定使用新式类
class calculator:
def count(self,args):
return 1
calc=calculator() #自定义类型
from random import choice
obj=choice(['hello,world',[1,2,3],calc]) #obj是随机返回的 类型不确定
print (type(obj) )
print (obj.count('a')) #方法多态
对于一个临时对象obj,它通过Python的随机函数取出来,不知道具体类型(是字符串、元组还是自定义类型),都可以调用count方法进行计算,至于count由谁(哪种类型)去做怎么去实现我们并不关心。
有一种称为”鸭子类型(duck typing)“的东西,讲的也是多态:
_metaclass_=type # 确定使用新式类
class Duck:
def quack(self):
print ("Quaaaaaack!")
def feathers(self):
print ("The duck has white and gray feathers.")
class Person:
def quack(self):
print ("The person imitates a duck." )
def feathers(self):
print ("The person takes a feather from the ground and shows it." )
def in_the_forest(duck):
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
game()
#输出
Quaaaaaack!
The duck has white and gray feathers.
The person imitates a duck.
The person takes a feather from the ground and shows it.
就in_the_forest函数而言,参数对象是一个鸭子类型,它实现了方法多态。但是实际上我们知道,从严格的抽象来讲,Person类型和Duck完全风马牛不相及。
2、运算符多态
def add(x,y):
return x+y
print (add(1,2)) #输出3
print (add("hello,","world")) #输出hello,world
print (add(1,"abc")) #抛出异常
TypeError: unsupported operand type(s) for +: 'int' and 'str'
上例中,显而易见,Python的加法运算符是”多态“的,理论上,我们实现的add方法支持任意支持加法的对象,但是我们不用关心两个参数x和y具体是什么类型。
Python同样支持运算符重载,实例如下:
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)
#输出:
Vector (7, 8)
一两个示例代码当然不能从根本上说明多态。普遍认为面向对象最有价值最被低估的特征其实是多态。
- 参考文献:
http://www.cnblogs.com/dolphin0520/archive/2013/03/29/2986924.html
http://www.cnblogs.com/jeffwongishandsome/archive/2012/10/06/2713258.html
继承
在C++和Java中,使用继承时,子类的构造函数会自动调用父类的构造函数,但在Python中,子类必须显式的在init()函数中再次调用父类中的init()函数。如下例:
一个类(subclass)可以借用另一个类(superclass)的行为
避免重复操作
提升代码复用程度
举例:继承
员工信息系统
Employee类 #包含所有员工通用一般信息
Employee类属性raisesalary() #返回员工工资信息
Employee子类:work()
共享raisesalary() 属性
自己work()属性
class Employee(object):
def __init__(self, name, salary = 0):
self.name = name
self.salary = salary
def raisesalary(self, percent):
self.salary = self.salary * (1 + percent)
def work(self):
print (self.name, "writes computer code" )
子类Designer也可以使用super来进行初始化。
class Designer(Employee):
def __init__(self, name):
super(Designer, self).__init__(name, 5000)
def work(self):
print self.name, "writes design document"
2.多继承
在C++中,使用虚继承来实现多继承,以避免子类在继承时多次调用基类的构造函数,而在Java中,则取消了多继承,使用接口来达到多继承的效果。在Python中的解决方案是MRO即Method Resolution Order,方法解析顺序。主要是通过super方法实现的。但如果用super方法来解决多继承问题,由于各个父类中的init()函数中参数的数量可能不同,那应该怎么初始化呢?如下例。
class A(object):
def __init__(self, a):
print a
class B(object):
def __init__(self, a, b):
print a+b
class C(A, B):
def __init__(self):
super(C,self).__init__(?)
c = C()