python 面向对象编程----封装&继承&多态_栈与队列封装&乌龟吃鱼游戏

python 面向对象编程介绍

一、面向过程编程

1. 定义

“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

面向对象的方法也是含有面向过程的思想。面向过程最重要的是模块化的思想方法。

2.特性、优点、缺点

特性:模块化 流程化

优点:性能比面向对象高,
因为类调用时需要实例化,开销比较大,比较消耗资源;
单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展

二、函数式编程(了解名词)

函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。

主要思想: 把运算过程尽量写成一系列嵌套的函数调用。

三、面向对象编程

1.定义

面向对象是按人们认识客观世界的系统思维方式,把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

2.特性、优点、缺点

特性: 抽象 封装 继承 多态

优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合 的系统,使系统更加灵活、更加易于维护

缺点:性能比面向过程低

3.对象和类

(1)如何定义类?

class 类():pass
类(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)调用父类的方法:
  1. 父类名.父类的方法名()
  2. 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 解释器可能会把 __ 属性名 改成不同的变量名的 优点
  1. 确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,
    代码更加健壮。
  2. 如果又要允许外部代码修改属性怎么办?可以给类增加专门设置属性方
    法。 为什么大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数。

# 私有属性和私有方法:

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帧的速度运行

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值