文章目录
python 面向对象编程介绍
一、面向过程编程
1. 定义
“面向过程”
(Procedure Oriented)是一种以过程为中心的编程思想。分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象
的方法也是含有面向过程的思想。面向过程最重要的是模块化的思想方法。
2.特性、优点、缺点
特性:模块化 流程化
优点:性能比面向对象高,
因为类调用时需要实例化,开销比较大,比较消耗资源;
单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
二、函数式编程(了解名词)
函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
主要思想: 把运算过程尽量写成一系列嵌套的函数调用。
三、面向对象编程
1.定义
面向对象是按人们认识客观世界的系统思维方式,把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
2.特性、优点、缺点
特性: 抽象 封装 继承 多态
优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合 的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
3.对象和类
(1)如何定义类?
类(Class) 是现实或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。
对象(Object) 是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。
类(Class) 是创建实例的模板
对象(Object) 是一个一个具体的实例
类和对象 的区别就是 猫和蓝猫 的区别。
(2)如何将类转换成对象?
对象名 = 类名(参数1,参数2…参数n)
实例化 是指在面向对象的编程中,用类创建对象的过程。
是将一个抽象的概念类,具体到该类实物的过程。
class Student(object):
"""封装学生类"""
def __init__(self, sid, name, score1, score2):
"""封装了学生属性信息"""
self.sid = sid
self.name = name
self.score1 = score1
self.score2 = score2
def show(self):
pass
def compute_sum_score(self):
return self.score1 + self.score2
def compute_avg_score(self):
return (self.score1 + self.score2)/2
stu1 = Student(sid='001', name='张三', score1=100, score2=90)
print(stu1.compute_sum_score())
stu2 = Student(sid='002', name='张三', score1=100, score2=100)
print(stu2.compute_avg_score())
四、面对对象三大特性–封装特性
面向对象的三大特性是指:封装、继承和多态
1. 封装
顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
所以,在使用面向对象的封装特性时,需要:
1). 将内容封装到某处
2). 从某处调用被封装的内容
1). 通过对象直接调用被封装的内容: 对象.属性名
2). 通过self间接调用被封装的内容: self.属性名
3). 通过self间接调用被封装的内容: self.方法名()
2.构造方法__init__与其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法
。自动执行构造方法里面的内容。
3.封装练习:
创建一个类People,拥有的属性为姓名, 性别和年龄, 拥有的方法为购物,玩游戏,学习;实例
化对象,执行相应的方法。
显示如下:
小明,18岁,男,去西安赛格购物广场购物
小王,22岁,男,去西安赛格购物广场购物
小红,10岁,女,在西部开源学习
提示:
属性:name,age,gender
方法:shopping(), playGame(), learning()
class People:
print("正在创建类对象..........")
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def shopping(self):
print("%s,%d岁,%s,去西安赛格购物广场购物" %(self.name, self.age, self.gender))
def playGame(self):
pass
def learning(self):
pass
# 当定义类的时候, 类内容是否会执行? 会执行
# stu1 = People('xx', 10, 'xxx')
定义函数,内容不执行;当定义类的时候, 类内容是否会执行? 会执行 如下结果:
结果:
五、面对对象三大特性–继承特性
面向对象的三大特性是指:封装、继承和多态
1. 单继承特性
继承描述的是事物之间的所属关系,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类、扩展类
(Subclass),而被继承的class称为基类、父类
或超类(Baseclass、Superclass)。
(1)如何实现继承?
子类在继承的时候,在定义类时,小括号()中为父类的名字。
(2)继承的工作机制是什么?
父类的属性、方法,会被继承给子类。
当实例化对象, 子类没有构造方法, 自动调用并执行父类的构造方法。
当子类调用的方法没有的时候, 自动去父类里面寻找并执行。
(3)重写父类方法:
就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。
(4)调用父类的方法:
- 父类名.父类的方法名()
- super(): py2.2+的功能
# 在Python代码中,没有指定父类, 默认继承object类。
# 父类: object 子类: Student
class Student(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def learn(self):
print("%s正在学习编程......." %(self.name))
# 父类: Student 子类: MathStudent
class MathStudent(Student):
# 重写父类的learn方法
# 重写父类方法: 就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。
def learn(self):
# 方法一: 调用父类的方法
# Student.learn(self)
# 方法二: 调用父类的方法, 自动帮当前类寻找父类的名称
super(MathStudent, self).learn()
print("%s正在学习英语四级....." %(self.name))
# 父类: Student 子类: EnglishStudent
class EnglishStudent(Student):
pass
s1 = Student("粉条", 10, 'male')
print(s1.name)
# 当实例化对象, 子类没有构造方法, 自动调用并执行父类的构造方法。
s2 = MathStudent("拉格朗日", 100, 'male')
# 当子类调用的方法没有的时候, 自动去父类里面寻找并执行。
s2.learn()
2. 私有属性和私有方法:
(1)私有属性和私有方法
在 Python 中,
实例的变量名如果 以__ 开头
,就变成了一个私有变量/属性
(private),
实例的函数名如果以 __ 开头
,就变成了一个私有函数/方法
(private)
只有内部可以访问,外部不能访问。
(2)私有属性一定不能从外部访问吗?
python2版本不能直接访问 __属性名
是因为 Python 解释器对外把 __属性名
改成了 _类名__属性名
,所以,仍然可以通过_类名__属性名
来访问
(3)不同版本的 Python 解释器可能会把 __ 属性名
改成不同的变量名的 优点
- 确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,
代码更加健壮。 - 如果又要允许外部代码修改属性怎么办?可以给类增加专门设置属性方
法。 为什么大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数。
# 私有属性和私有方法:
class Student(object):
"""
需求: 学生成绩保密, 外部不可以访问分数, 只可以访问分数的等级
"""
def __init__(self, name, age, score):
self.name = name
self.age = age
# self.__score是私有属性, 只能在类的内部访问, 类的外部不可以访问。
self.__score = score
# __get_level是私有方法,只能在类的内部访问, 类的外部不可以访问。
def __get_level(self):
if 90 <= self.__score <= 100:
return "优秀"
elif 80 <= self.__score < 90:
return "良好"
elif 60 <= self.__score < 80:
return "及格"
else:
return "不及格"
stu1 = Student("粉条", 10, 59)
# print(stu1.__score) # 调用私有属性失败
# print("学生分数的等级: ", stu1.__get_level()) # 调用私有方法失败
# Python解释器自动将私有属性和私有方法重命名了, 命名方式一般是_类名__属性名、_类名__方法名
print(stu1._Student__score)
print(stu1._Student__get_level())
3.多继承:即子类有多个父类,并且具有它们的特征
(1) 新式类与经典类
在Python 2及以前的版本中,由任意内置类型派生出的类,都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。
“新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python3.x之后的版本
,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。
(2) 新式类与经典类的 不同
最明显的区别在于继承搜索的顺序不同,即:
经典类
多继承搜索顺序(深度优先算法)
:先深入继承树左侧查找,然后再返回,开始查找右侧。
新式类
多继承搜索顺序(广度优先算法)
:先在水平方向查找,然后再向上查找,
六、面对对象三大特性–多态特性
1.定义
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。通俗来说: 同一操作作用于不同的对象,
可以有不同的解释,产生不同的执行结果。
2.多态的好处
当我们需要传入更多的子类,只需要继承父类就可以了,而方法既可以直接不重写(即使用父类的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节,而当我们新增一种的子类时,只要确保新方法编写正确,而不用管原来的代码。
这就是著名的“开闭”原则:
对扩展开放(Open for extension):允许子类重写方法函数
对修改封闭(Closed for modification):不重写,直接继承父类方法函数
七、项目案例
1.栈与队列的封装
(1)栈
栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为“栈顶”,另一固定端称为“栈底”,当栈中没有元素时称为“空栈”。向一个栈内插入元素称为是进栈,push;从一个栈删除元素称为是出栈,pop。
特点 :后进先出(LIFO)。
class Stack(object):
"""
根据列表的数据结构封装栈的数据结构
属性: 栈元素 stack
方法: 获取栈顶元素: get_top()
获取栈底元素; get_bootom()
入栈: push()
出栈: pop()
"""
def __init__(self):
self.stack = [] # [1, 2, 3]
def get_top(self):
return self.stack[-1]
def get_bootom(self):
return self.stack[0]
def push(self, item): # 入栈,有一个入栈元素 item
self.stack.append(item)
return True
def pop(self): # 出栈 出最后一个元素 pop()方法默认删除最后一个元素
item = self.stack.pop() # 返回删除的元素
return item
def show(self):
return self.stack
# 定义魔术方法, 获取长度
def __len__(self):
return len(self.stack) # self.stack 是个列表 所以这里可以用 len()
# 实例化对象
if __name__ == '__main__':
st = Stack()
print(st.show())
st.push(1)
st.push(2)
st.push(3)
print("入栈后:",st.show())
item = st.pop()
print("出栈元素是:", item)
print("出栈后: ",st.show())
print("栈的长度: ", len(st)) # 此处时自己定义的类, 需要定义 def __len__(self): 方法,才能使用此方法求长度。
# 在list或者string中,它们的类中定义了求长度的方法,所以可以直接len()求长度
# print('栈元素个数:',stack.__len__()) # 为了好看不这样写,写成上一句
print("栈顶元素: ", st.get_top())
print("栈底元素: ", st.get_bootom())
"""
以前能用len()方法获取长度,是因为在那些list ,string 这些类里,自己有__len__ 这个魔术方法,可以用len()来求长度
而现在 咱自己定义的这个类里没有 __len__ 这个函数,所以不能用 len()方法来获取长度 ,所以要将这个函数定义出来
def __len__(self):
return len(self.stack)
然后才能用 len() 去获取长度
"""
结果:
(2)队列
队列是限制在一端进行插入操作和另一端删除操作的线性表,允许进行插入操作的一端称为“队尾”,允许进行删除操作的一端称为“队头”,当队列中没有元素时称为“空队”。
特点 :先进先出(FIFO)。
class queue(object):
"""
根据列表的数据结构封装队列的数据结构
属性: 队列元素 queue
方法: 获取队首元素: get_first()
获取队尾元素; get_end()
入队: enqueue()
出队: dequeue()
"""
def __init__(self):
self.queue = []
def get_first(self):
return self.queue[0]
def get_end(self):
return self.queue[-1]
def enqueue(self, item): # 入队
self.queue.append(item)
return True
def dequeue(self): # 出队 出第一个元素
item = self.queue.pop(0) # 返回删除的元素
return item
def show(self):
return self.queue
# 定义魔术方法, 获取长度
def __len__(self):
return len(self.queue)
# 实例化对象
if __name__ == '__main__':
q = queue()
print(q.show())
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print("入队后:",q.show())
item = q.dequeue()
print("出队元素是:", item)
print("出队后: ",q.show())
print("队列的长度: ", len(q))
print("队首元素: ", q.get_first())
print("队尾元素: ", q.get_end())
结果:
2.二叉树节点的封装
先序遍历(根左右):1->2->4->5->7->3->6
中序遍历(左根右):4->2->7->5->1->3->6
后序遍历(左右根);4->7->5->2->6->3->1
class Node(object):
def __init__(self, data, lchild=None, rchild=None):
self.data = data
self.lchild = lchild
self.rchild = rchild
# 魔术方法: len(), __len__. str(), __str__
def __str__(self): # 友好的字符串显示信息
return 'Node<%s>' % (self.data)
# #字符串的友好表达, 输出 A-left: Node<B>
# 屏蔽这个魔术方法 ,则会输出 A-left: <__main__.Node object at 0x7f9cf9a2c9d0>
def pre_view( root):
"""
先序遍历: 根节点-左子树节点-右子树节点
传递根节点
:param root:
:return:
"""
if root == None:
return
print(root.data)
pre_view(root.lchild)
pre_view(root.rchild)
def last_view(root):
"""
后序遍历: -左子树节点-右子树节点-根节点
传递根节点
:param root:
:return:
"""
if root == None:
return
last_view(root.lchild)
last_view(root.rchild)
print(root.data)
def mid_view(root):
"""
中序遍历: -左子树节点--根节点-右子树节点
传递根节点
:param root:
:return:
"""
if root == None:
return
mid_view(root.lchild)
print(root.data)
mid_view(root.rchild)
if __name__ == '__main__':
D = Node('D')
B = Node('B', D)
C = Node('C')
A = Node('A', B, C)
# print("A-left: ", str(A.lchild))
# print("A-right: ", str(A.rchild))
# 先序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)
print("先序遍历:")
pre_view(A)
print("后序遍历:")
last_view(A)
print("中序遍历:")
mid_view(A)
结果:
八、项目案例: 乌龟吃鱼游戏
1.Pygame游戏的基本操作
Pygame是跨平台Python模块,专为电子游戏设计,包含图像、声音。允许实时电子游戏研发而无需被低级语言(如机器语言和汇编语言)束缚。
一个游戏循环(也可以称为主循环)就做下面这三件事:
处理事件
更新游戏状态
绘制游戏状态到屏幕上
import pygame
# 1). 初始化pygame
pygame.init()
# 2). 显示游戏界面
pygame.display.set_mode((800, 800))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("游戏结束......")
exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
print("UP")
elif event.key == pygame.K_DOWN:
print('DOWN')
2.乌龟吃鱼游戏
游戏规则:
1). 假设游戏场景为范围(x,y)为0<=x<=10,0<=y<=10
2). 游戏生成1只乌龟和10条鱼, 它们的移动方向均随机
3). 乌龟的最大移动能力为2(它可以随机选择1还是2移动),
鱼儿的最大移动能力是1当移动到场景边缘,自动向反方向移动
4). 乌龟初始化体力为100(上限), 乌龟每移动一次,体力消耗1
当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20, 鱼暂不计算体力
5). 当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束
import random
import time
import pygame
import sys
from pygame.locals import * # 导入一些常用的函数
width = 474
height = 233
pygame.init()
screen = pygame.display.set_mode([width, height])
pygame.display.set_caption('乌龟吃鱼') # 定义窗口的标题为'乌龟吃鱼'
background = pygame.image.load("./img/bg.jpg").convert()
fishImg = pygame.image.load("./img/hot-dog.png").convert_alpha()
wuguiImg = pygame.image.load("./img/sicong.png").convert_alpha()
# 乌龟吃掉小鱼的音乐 mp3格式的不行,wav格式的
# eatsound = pygame.mixer.Sound("achievement.wav")
# 背景音乐
pygame.mixer.music.load("./img/game_music.mp3")
pygame.mixer.music.play(loops=0, start=0.0)
# 成绩文字显示
count = 0
font = pygame.font.SysFont("arial", 20)
score = font.render("score %d" % count, True, (255, 255, 255))
# 显示游戏状态
status = font.render("Gaming", True, (255, 255, 255))
w_width = wuguiImg.get_width() - 5 # 得到乌龟图片的宽度,后面留着吃鱼的时候用
w_height = wuguiImg.get_height() - 5 # 得到乌龟图片的高度
y_width = fishImg.get_width() - 5 # 得到鱼图片的宽度
y_height = fishImg.get_height() - 5 # 得到鱼图片的高度
fpsClock = pygame.time.Clock() # 创建一个新的Clock对象,可以用来跟踪总共的时间
# 乌龟类
class Turtle:
def __init__(self):
self.power = 100 # 体力
# 乌龟坐标
self.x = random.randint(0, width - w_width)
self.y = random.randint(0, height - w_height)
# 乌龟移动的方法:移动方向均随机 第四条
def move(self, new_x, new_y):
self.x = new_x % width
self.y = new_y % height
self.power -= 1 # 乌龟每移动一次,体力消耗1
def eat(self):
self.power += 20 # 乌龟吃掉鱼,乌龟体力增加20
if self.power > 100:
self.power = 100 # 乌龟体力100(上限)
# 鱼类
class Fish:
def __init__(self):
# 鱼坐标
self.x = random.randint(0, width - y_width)
self.y = random.randint(0, height - y_height)
def move(self):
new_x = self.x + random.choice([-10])
self.x = new_x % width
tur = Turtle() # 生成1只乌龟
fish = [] # 生成10条鱼
for item in range(10):
newfish = Fish()
fish.append(newfish) # 把生成的鱼放到鱼缸里
# pygame有一个事件循环,不断检查用户在做什么。事件循环中,如何让循环中断下来(pygame形成的窗口中右边的插号在未定义前是不起作用的)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
# 通过上下左右方向键控制乌龟的动向
if event.key == pygame.K_LEFT:
tur.move(tur.x - 10, tur.y)
if event.key == pygame.K_RIGHT:
tur.move(tur.x + 10, tur.y)
if event.key == pygame.K_UP:
tur.move(tur.x, tur.y - 10)
if event.key == pygame.K_DOWN:
tur.move(tur.x, tur.y + 10)
screen.blit(background, (0, 0)) # 绘制背景图片
screen.blit(score, (400, 20)) # 绘制分数
screen.blit(status, (0, 20)) # 绘制分数
# 绘制鱼
for item in fish:
screen.blit(fishImg, (item.x, item.y))
# pygame.time.delay(100)
item.move() # 鱼移动
screen.blit(wuguiImg, (tur.x, tur.y)) # 绘制乌龟
# 判断游戏是否结束:当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束
if tur.power < 0:
print("Game Over: Turtle power is 0")
# 显示游戏状态
status = font.render("Game Over: Turtle power is 0", True, (255, 255, 255))
pygame.display.update() # 更新到游戏窗口
time.sleep(1)
sys.exit(0)
elif len(fish) == 0:
status = font.render("Game Over: Fish is empty", True, (255, 255, 255))
pygame.display.update() # 更新到游戏窗口
sys.exit(0)
for item in fish:
# print("鱼", item.x, item.y, y_width, y_height)
# print("乌龟", tur.x, tur.y, w_width, w_height)
# 判断鱼和乌龟是否碰撞?
if ((tur.x < item.x + y_width) and (tur.x + w_width > item.x)
and (tur.y < item.y + y_height) and (w_height + tur.y > item.y)):
fish.remove(item) # 鱼死掉
# 吃鱼音乐
# eatsound.play()
count = count + 1 # 累加
score = font.render("score %d" % count, True, (255, 255, 255))
# print("死了一只鱼")
# print("乌龟最新体力值为 %d" % tur.power)
pygame.display.update() # 更新到游戏窗口
fpsClock.tick(10) # 通过每帧调用一次fpsClock.tick(10),这个程序就永远不会以超过每秒10帧的速度运行