一、面向对象编程介绍
1、面向过程编程
1、“面向过程”是一种以过程为中心的编程思想。这些都是以什么正在发生为目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。
2、特性:模块化、流程化
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源,单片机、嵌入式开发、Linux/Unix等一般采用面向过程编程,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
2、函数式编程
1、函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算,而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
主要思想:把运算过程尽量写成一系列嵌套的函数调用。
3、面向对象编程
1、面向对象是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。
2、特性:抽象 封装 继承 多态
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
二、面向对象编程三大特性
1、对象和类
1、类(Class) 是现实或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。是创建实例的模版
对象(Object) 是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。 是一个一个地实例
2、 使用class类():pass 定义类
3、实例化 是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。实例化过程中一般由类名 对象名 = 类名(参数1,参数2…)构成。
2、封装特性
1、封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
1). 将内容封装到某处
2). 从某处调用被封装的内容
- 通过对象直接调用被封装的内容: 对象.属性名
- 通过self间接调用被封装的内容: self.属性名
- 通过self间接调用被封装的内容: self.方法名()
2、构造方法__init__与其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。自动执行构造方法里面的内容。
3、 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过
对象直接或者self间接获取被封装的内容。
如下创建一个类它有属性,最后实例化执行过程。
class People:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def shopping(self):
print('%s %s %s去赛格购物'%(self.name,self.age,self.gender))
def playGame(self):
print('%s %s %s在打游戏'%(self.name,self.age,self.gender))
def leanng(self):
print('%s %s %s在学习'%(self.name,self.age,self.gender))
people = People('小明',20,'男')
people.shopping()
people.leanng()
3、继承特性
1、继承描述的是事物之间的所属关系,当我们定义一个class的时候,可以从某个现有的class
继承,新的class称为子类、扩展类(Subclass),而被继承的class称为基类、父类或超类(Baseclass、Superclass)。
2、子类在继承的时候,在定义类时,小括号()中的父类名字
3、父类的属性、方法会被继承给子类。举例如下:如果子类没有定义__init__方法,父类有,那么在子类继承父类的时候这个方法就被继承了,所以只要创建对象,就默认执行了那个继承过来的__init__方法。
4、 重写父类方法: 就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。
5、调用父类的方法:
- 父类名.父类的方法名()
- super(): py2.2+的功能
6、在Python 2及以前的版本中,由任意内置类型派生出的类,都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。 “新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。
7、经典类 多继承搜索顺序(深度优先算法):先深入继承树左侧查找,然后再返回,开始查找右侧。
新式类 多继承搜索顺序(广度优先算法):先在水平方向查找,然后再向上查找,
8、实例的变量名如果以 __ 开头,就变成了一个私有变量/属性(private),实例的函数名如果以 __ 开头,就变成了一个私有函数/方法(private)只有内部可以访问,外部不能访问。
Python 解释器对外把 __属性名 改成了 _类名__属性名 ,所以不可以访问,可以通过 _类名__属性名 来访问 __属性名 。
4、多态特性
1、 多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。通俗来说: 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
2、多态的好处就是,当我们需要传入更多的子类,只需要继承父类就可以了,而方法既可以直接不重写(即使用父类的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不用细节,而当我们新增一种子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的“开闭原则”
- 对扩展开放:允许子类重写方法 函数
- 对修改封闭:不重写,直接继承父类的方法函数
5、项目案例:栈与队列的封装
1、栈是限制在一端进行的插入操作和删除操作的线性表(俗称堆栈),允许进行的 操作的一端称为“栈顶,另一固定端称为”栈底”,当栈种没有元素的时称”空栈插入元素称为是“进栈“,push;从一个栈删除元素是pop。特点,后进后出。
2、队列是限制在一端进行插入操作和另一端删除操作的线性表,允许进行插入的一端称为“队尾”,允许进行删除操作的一端称为“队头”,,当队列中没有元素时称为“空队”。特点 :先进先出(FIFO)
以下代码实现了队列的进栈、出栈、栈元素等
def __init__(self):
self.list_queue = []
def enqueue(self,item):
self.list_queue.append(item)
print('进栈成功!')
def dequeue(self):
if not self.is_empty():
self.list_queue.pop()
print('出栈成功!')
else:
print('栈为空!')
def len(self):
len_queue = len(self.list_queue)
print('栈的长度为%s'%(len_queue))
def is_empty(self):
if len(self.list_queue) == 0:
return True
else:
print('当前栈为:',queue.list_queue)
def first(self):
if not self.is_empty():
first = self.list_queue[-1]
print('栈顶元素为%s'%(first))
else:
print('栈为空!')
queue = Queue()
#向栈中逐渐加入五个元素
queue.enqueue(5)
queue.enqueue(6)
print('当前栈为:',queue.list_queue)
#查看栈的长度
queue.len()
#获取栈顶元素
queue.first()
#删除栈元素
queue.dequeue()
queue.dequeue()
print('当前栈为:',queue.list_queue)
#判断栈是否为空
queue.is_empty()
6、项目案例乌龟吃鱼游戏
import time
import pygame
import sys
from pygame.locals import * # 导入一些常用的函数
width = 640
height = 480
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/fish.png").convert_alpha()
wuguiImg = pygame.image.load("./img/turtle.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", 40)
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):
# 判断移动后是否超出边界
if new_x < 0:
self.x = 0 - new_x
elif new_x > width:
# self.x=width-(new_x-width)
self.x = 0
else:
# 不越界则移动乌龟的位置
self.x = new_x
if new_y < 0:
self.y = 0 - new_y
elif new_y > height:
# self.y=height-(new_y-height)
self.y = 0
else:
# 不越界则移动乌龟的位置
self.y = new_y
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])
if new_x < 0:
self.x = width
else:
self.x = new_x
# 开始测试数据
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 == 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, (500, 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)
三、面向对象高级特性
1、类属性就是类对象所拥有的属性,它被所有类对象的实列对象所共有,在内存种只存在一个副本。
2、类方法
- 类方法是类对象所拥有的方法,需要用装饰器一般以@classmethod来标识其为类方法。
- 1)对于类方法,第一个参数必须是类对象,作为第一个参数(cls是形参,可以修改为其他变量名,但是最好用“cls“)
- 2)能够通过实例对象和类对象去访问。
3、静态方法
静态方法需要装饰器一般以@classmethod来标识其为静态方法
1)静态方法不需要多定义参数
2)能够通过实例对象和类对象去访问。
4、单例模式
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要