Skip to content

【练习】英雄游戏

英雄游戏

简介

实战思路

类与实例

  • 相关知识点:类的定义、 属性、 方法
  • 需求: 1. 定义一个英雄类,此英雄类需要包含 姓名、 血量、 攻击力、 还需要有一个方法为讲台词。
# 定义类
class Hero:
    # 定义类属性
    name = ""
    hp = 0
    power = 0

    # 定义方法
    def speak(self):
        print(f"欢迎来到英雄联盟,我的名字是{self.name}")
# 类的实例化
hero = Hero()
# 实例对象.类属性 => 即可获取类属性
print(hero.name)
# 实例对象.方法名 => 即可调用实例
hero.speak()
  • 相关知识点:构造函数、实例对象、实例属性、实例方法
  • 需求: 1. 根据英雄类,实例化不同的英雄对象。 2. 每个英雄需要在实例化的时候,就有自己的姓名、攻击力、血量
class Hero:
    def __init__(self, name, hp, power):
        # 实例属性在构造函数内被初始化
        self.name = name
        self.hp = hp
        self.power = power

    # 定义方法
    def speak(self):
        print(f"欢迎来到英雄联盟,我的名字是{self.name},我的血量为{self.hp}")


jinx = Hero("jinx", 1000, 100)
# 实例对象.方法名 => 即可调用实例
jinx.speak()
  • 实例和类的关系总结: 1. 类只能获取、修改类变量。 1. 类不能直接调用实例方法,实例可以直接调用实例方法。 1. 实例对象可以获取类变量,但是当类变量和实例变量同名时,就只能获取到实例变量。 1. 实例不能修改类变量,可以修改获取实例变量。 1. self 就是实例本身。

封装、继承、多态

  • 相关知识点:封装。
  • 需求:每个英雄的血量不可以直接被获取或者修改。
class Hero:
    def __init__(self, name, hp, power):
        self.name = name
        # 私有属性
        self.__hp = hp
        self.power = power
jinx = Hero("jinx", 1000, 100)
# 报错
jinx.hp = 2000
  • 相关知识点:继承、调用父类属性、父类方法、重写父类属性、方法、super。
  • 需求1: 1. 现在除了英雄类,还有一种类是法师类。 1. 法师类继承于 Hero 类。 1. 法师类多了魔力的属性 1. 法师类多了一个放技能的方法。
  • 需求2: 1. 定义战士类。 1. 战士类继承于Hero类。 1. 战士的会多一个护甲的属性。 1. 战士在初始化的时候需要多传入一个护甲信息。
class APCHero(Hero):
    def __init__(self, name, hp, power, mp):
        super().__init__(name, hp, power)
        self.mp = mp

    def speak(self):
        # print(f"欢迎来到英雄联盟,我的名字是{self.name},我的血量为{self.hp}")
        super().speak()
        print("我是大美女")

    def charm(self):
        if self.mp < 50:
            print("蓝量不足")
        else:
            print("施展魅惑技能")
            self.mp -= 50


diaochan = APCHero("貂蝉", 1200, 80, 70)
diaochan.speak()
diaochan.charm()
  • 相关知识点:多态、 类型注解、导包。
  • 需求: 1. 定义一个单独的 fight 函数。 1. 在打斗之前,需要两个英雄先讲出台词。 1. 这个fight函数要求实现两个英雄的多轮回合制对打功能。最后需要返回一个赢家。 1. 创建一个测试用例文件,导入被测函数,并对它完成单元测试。
def fight(hero1: Hero, hero2: Hero):
    hero1.speak()
    hero2.speak()
    hero1_hp = hero1.hp
    hero2_hp = hero2.hp
    hero1_name = hero1.name
    hero2_name = hero2.name
    while True:
        hero1_hp = hero1_hp - 10
        hero2_hp = hero2_hp - 10
        # 当第一个英雄的血量小于0 或 当第二个英雄的血量小于0
        if hero1_hp <= 0 or hero2_hp <= 0:
            if hero1_hp > hero2_hp:
                # 字面量插值 - 字符串
                print(f"英雄{hero1_name}赢了")
                return hero1_name
            elif hero1_hp < hero2_hp:
                print("英雄赢了", hero2_name)
                return hero2_name
            else:
                return "平局"

def test_fight():
    jinx = Hero("jinx", 1000, 100)
    diaochan = APCHero("貂蝉", 1200, 80, 70)
    fight(jinx, diaochan)

设计模式

  • 相关知识点: 不定长参数、工厂设计模式、 静态方法、类方法。
  • 需求: 1. 现在多了一个同事小林,小林需要调用各种英雄初始化的方法,去完成他自己的逻辑。但是小林并不知道我设计了多少个类型的英雄。 1. 所以小林需要我将我目前所有的英雄类都放在一个工厂方法中进行初始化,传入不同的参数信息,返回不同的实例对象。如此一来,小林便不需要了解细节,只需要传入对应的参数获取对应的英雄即可。
class HeroFactory:
    @staticmethod
    def create_hero(hero_type, *args):
        if hero_type == "apc":
            return APCHero(*args)
        elif hero_type == "adc":
            # ADCHero(*args)
            print("adc")
        else:
            return Hero(*args)
三种方法对比
名称 定义 调用 关键字 使用场景
普通方法 至少需要一个参数self 实例名.方法名() 方法内部涉及到实例对象属性的操作
类方法 至少需要一个cls参数 类名.方法名() 或者实例名.方法名() @classmethod 如果需要对类属性,即静态变量进行限制性操作
静态方法 无默认参数 类名.方法名() 或者实例名.方法名() @staticmethod 无需类或实例参与

面试题

is和==的区别

在Python中,万物皆对象,而对象的三个基本要素:

  • 内存地址
  • 数据类型

is== 都作为常用的判断语句去进行使用,这两者之间的主要区别是:

  • == 运算符: 只比较两个对象的值,相同返回True,不同返回False。
  • is 运算符: 比较两个对象的id,相同返回True,不同返回False。

在这种场景下, 两个判断的执行结果均为True。

a, b = 1,1
# 判断a,b 是否相等
print(a == b)
print(a is b)
a = [1,2,3]
b = [1,2,3]
# 对比值一致,返回True
print(a == b)
# 对比内存地址不一致,返回False
print(a is b)

Python深拷贝与浅拷贝

a = [1,2,3,[2,3,[4]]]
# 浅拷贝,此时内部嵌套的列表,b是直接引用的其内存地址。
b=a
# 此时修改内部嵌套列表对应的值
b[3][2][0]=1
# 会发现a内部嵌套的列表也被同步修改


# 深拷贝
b = deepcopy(a)
b[3][2][0] = 2
# 会发现b内部嵌套的列表不变
# ===
c = 1
d = 1
# True,不可变类型是值传递,所以共享的一个内存地址
id(c) == id(d)
# 不可变
c.copy() # 报错

Python元组和列表的区别

元祖和列表在Python中,都是有序的,可迭代的数据结构。

其中列表支持的操作较多,比如增删查改都是支持的。

元祖相较于列表本质的区别就在于元祖是不可变的数据结构,比如列表常用的增删改,在元祖这个数据结构中,原则上都是不可实现的。

  • 修改、添加、删除操作
list_demo = [1, 2, 3]
tuple_demo = (1, 2, 3)
# 修改操作
list_demo[0] = "a" #成功
tuple_demo[0] = "a"  #报错
# 添加操作
list_demo.append("b") #成功
# 元祖没有可以添加的方法
# 删除操作
list_demo.remove(1) #成功
# 元祖没有可以删除的方法
  • 访问、切片、遍历操作
list_demo = [1, 2, 3]
tuple_demo = (1, 2, 3)
# 访问
print(list_demo[0])
print(tuple_demo[0])
# 切片: 获取0~1位的元素
print(list_demo[:2])
print(tuple_demo[:2])
# 遍历
for i in list_demo:
    print(i)
for i in tuple_demo:
    print(i)
  • 占用内存
from sys import getsizeof
list_demo = [1, 2, 3]
tuple_demo = (1, 2, 3)
# 同样的元素,同样的数量,元祖小于列表
print(getsizeof(list_demo))
print(getsizeof(tuple_demo))
对比 元祖 列表
定义 (1, 2, 3) [1, 2, 3]
修改 原则上不支持 支持
添加 不支持 支持
删除 不支持 支持
索引访问 支持 支持
切片 支持 支持
遍历 支持 支持
应用场景 固定的,不会被修改的数据 不固定的,可以被修改的数据
占用内存 较小 较大