小甲鱼《零基础学习Python》课后笔记(三十八):类和对象——继承

时间:2022-06-17
本文章向大家介绍小甲鱼《零基础学习Python》课后笔记(三十八):类和对象——继承,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

测试题: 0.继承机制给程序猿带来的最明显的好处是? 写一个新类时可以从旧类继承,不用重复编写,减少重复劳动。

1.如果按以下方式重写魔法方法__init__,结果会怎么样?

class Myclass:
	def __init__(self):
		return "I love FishC.com!"

会报错,因为__init__不能返回除None的其他对象。

>>> a = Myclass()
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    a = Myclass()
TypeError: __init__() should return None, not 'str'

2.当子类定义了与父类相同名字的属性或方法,Python是否会自动删除父类的相关属性和方法? 不会删除!Python的做法跟其他大部分面向对象编程语言一样,都是将父类属性或方法覆盖,子类对象调用的时候调用到覆盖后的新属性或方法,但父类的任然存在,只是子类“看不到”。

3.假设已经有鸟类的定义,现在我要定义企鹅类继承于鸟类,但我们知道企鹅是不会飞的,我们应该如何屏蔽父类(鸟类)中飞的方法? 覆盖父类方法,例如将函数体内容写pass,这样调用fly方法就没有任何反应了。

4.super函数有什么“超级”的地方? super函数超级之处在于你不需要明确给出任何基类的名字,它会自动帮你找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着你如果需要改变类继承关系,你只需要改变class语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。

5.多重继承使用不当会导致重复调用(也叫钻石继承、菱形继承)的问题,请分析以下代码在实际编程中有可能导致什么问题?

class A():
	def __init__(self):
		print("进入A...")
		print("离开A...")

class B(A):
	def __init__(self):
		print("进入B...")
		A.__init__(self)
		print("离开B...")

class C(A):
	def __init__(self):
		print("进入C...")
		A.__init__(self)
		print("离开C...")

class D(B,C):
	def __init__(self):
		print("进入D...")
		B.__init__(self)
		C.__init__(self)
		print("离开D...")

多继承容易导致重复调用问题,上例中实例化D类后A被前后进入了两次。

>>> d = D()
进入D...
进入B...
进入A...
离开A...
离开B...
进入C...
进入A...
离开A...
离开C...
离开D...

6.如何解决上一题中出现的问题? 使用super()方法。

class A():
	def __init__(self):
		print("进入A...")
		print("离开A...")

class B(A):
	def __init__(self):
		print("进入B...")
		super().__init__()
		print("离开B...")

class C(A):
	def __init__(self):
		print("进入C...")
		super().__init__()
		print("离开C...")

class D(B,C):
	def __init__(self):
		print("进入D...")
		super().__init__()
		print("离开D...")

结果就不会重复调用了

>>> e = D()
进入D...
进入B...
进入C...
进入A...
离开A...
离开C...
离开B...
离开D...

动动手 0.定义一个点(Point)类和直线(Line)类,使用getLen方法可以获得直线的长度。

提示: 1.设点A(X1,Y1)、点B(X2,Y2),则两点构成的直线长度|AB| = (x1−x2)2+(y1−y2)2)sqrt{(x1-x2)^2+(y1-y2)^2 )}(x1−x2)2+(y1−y2)2)​ 2.Python中计算开根号可使用math模块中的sqrt函数 3.直线需有两点构成,因此初始化时需有两个点(Point)对象作为参数

import math
class Point(object):
	"""docstring for Point"""
	def __init__(self, x = 0, y = 0):
		self.x = x
		self.y = y

	def getX(self):
		return self.x

	def getY(self):
		return self.y

class Line(Point):
	"""docstring for Line"""
	def __init__(self, p0, p1):
		self.x = p1.getX() - p0.getX()
		self.y = p1.getY() - p0.getY()
		self.len = math.sqrt(self.x * self.x + self.y * self.y)

	def getlen(self):
		return self.len

测试结果:

>>> p0 = Point(1,5)
>>> p1 = Point(2,6)
>>> line = Line(p0, p1)
>>> line.getlen()
1.4142135623730951

1.展示一个你的作品:你已经掌握了Python大部分知识,要开始自食其力了!请画一个星期做一个你能做出的最好的作品(可以是游戏、应用软件、脚本),使用上你学过的任何东西(类,函数,字典,列表……)来改进你的程序。 这次写了一个外星人飞船游戏,上下左右键控制移动,空格键发射炮弹,后续还会继续改进。

总共有5个python文件:alien_invasion.py,bullet.py,ship.py,settings.py,game_functions.py。

①主程序文件:alien_invasion.py

import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    # 创建一艘飞船
    ship = Ship(ai_settings, screen)
    # 创建一个用于存储子弹的编组
    bullets = Group()
    # 开始游戏主循环
    while True:
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, bullets)
run_game()

②炮弹:bullet.py

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""

    def __init__(self, ai_settings, screen, ship):
        """在飞船所在的位置创建一颗子弹对象"""
        super(Bullet, self).__init__()
        self.screen = screen

        # 在(0,0)处创建一个子弹矩形,在设置正确的位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top

        # 存储用小数表示的子弹位置
        self.y = float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

    def update(self):
        """向上移动子弹"""
        # 更新表示子弹位置的小数值
        self.y -= self.speed_factor
        # 更新表示子弹的rect的位置
        self.rect.y = self.y

    def draw_bullet(self):
        """绘制子弹"""
        pygame.draw.rect(self.screen, self.color, self.rect)

③飞船:ship.py

import pygame
class Ship():
    def __init__(self, ai_settings,  screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings
        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()

        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        # 在飞船属性center中存储小数值
        self.center = float(self.rect.centerx)

        # 在飞船属性bottomx中存储小数值
        self.bottomx = float(self.rect.bottom)

        # 移动标志
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False

    def update(self):
        """根据移动标志调整飞船的位置"""
        # 更新飞船的center值而不是rect
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
        if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
            self.bottomx += self.ai_settings.ship_speed_factor
        if self.moving_up and self.rect.top > 0:
            self.bottomx -= self.ai_settings.ship_speed_factor

        # 根据self.center更新rect对象
        self.rect.centerx = self.center
        self.rect.bottom = self.bottomx

    def blitme(self):
        """在指定位置创建飞船"""
        self.screen.blit(self.image, self.rect)

④设置文件:settings.py

class Settings():
    """存储外星人入侵的所有设置"""

    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 800
        self.screen_height = 500
        self.bg_color = (230, 230, 230)
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 3

⑤功能函数:game_functions.py

import sys

import pygame
from bullet import Bullet

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键按下"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_UP:
        ship.moving_up = True
    elif event.key == pygame.K_DOWN:
        ship.moving_down = True
    elif event.key == pygame.K_SPACE:
        fire_bullet(ai_settings, screen, ship, bullets)

def fire_bullet(ai_settings, screen, ship, bullets):
    # 创建一颗子弹,并将其加入到编组bullets中
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

def check_keyup_events(event, ship):
    """响应按键松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False
    elif event.key == pygame.K_UP:
        ship.moving_up = False
    elif event.key == pygame.K_DOWN:
        ship.moving_down = False

def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def update_screen(ai_settings, screen, ship, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()


def update_bullets(bullets):
    """更新子弹的位置,并删除已经消失的子弹"""
    # 更新子弹位置
    bullets.update()

    # 删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

零基础入门Python系列文章也要画上一个句号了,接下我将学习Python爬虫和数据处理方面的内容,感谢大家!