面向对象实战

上周,我们学习了类的各种方法,并认识到了类是怎样实现封装性的。本周,我们继续学习类的另外两大特性,继承性和多态性。

类的继承和多态在实际项目中非常普遍,也非常容易理解,它能帮助你编写出结构清晰、功能惊艳的程序。

知识结构
在这里插入图片描述

类的继承性

object类

回忆第10周我们初次接触类的概念的时候老师说过,“在当前类不继承任何父类的时候,括号里可以什么也不写,这时当前类默认继承Python的根类,叫做object类。
因此,object类可以看做是Python的上帝类,是所有类的“祖先”。现在,我们就从认识object类开始,学习类的继承性。
object类里面定义了一些魔法变量和魔法方法,这些可以被所有的类继承。
回忆魔法变量__dict__的用法??找同学回答

我们再讲一个Python的内置方法dir(),dir()的语法规则是dir(o),其中o是一个类,dir(o)返回的是类o所有的方法。
任务:现在我们来创建一个猫科动物类FelidAnimal,这个类暂时什么都不做(使用pass占位符),来看看此时的FelidAnimal类都有哪些方法和变量。

class FelidAnimal():
    pass
print("FelidAnimal的变量有%s" % FelidAnimal.__dict__)
print("FelidAnimal的方法有%s" % dir(FelidAnimal))
FelidAnimal的变量有{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'FelidAnimal' objects>, '__weakref__': <attribute '__weakref__' of 'FelidAnimal' objects>, '__doc__': None}
FelidAnimal的方法有['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

结论:即便是类体重什么都没有定义,类还是有很多变量和方法,并且这些变量和方法都是魔法变量和魔法方法。
显然,它们都是继承自FelidAnimal的父类,也就是object类。
我们现在讲解object类中一个比较重要的方法__str__()方法(注意str前后各有两个英文下划线)。__str__方法定义了当print(实例名)时,打印的实例的信息。

默认情况下,print(实例名)打印的是实例所属类和内存地址,这一点我们已经见识过了。不过还是实验一下更加稳妥。

class FelidAnimal():
    pass
# 构造一个实例
my_animal = FelidAnimal()
# 打印实例信息
print(my_animal)
<__main__.FelidAnimal object at 0x000001D19D836408>

对于大部分人来说,这种打印出类名和内存地址的方式是没什么意义的,人们更想打印出一些具体的信息,比如这个猫科动物类的身高、体重等。
在类的多态性那一节,老师会跟你讲解你可以重写__str__来实现这个目的。

单继承

类的继承的定义:类的继承性是指当前类能够保留父类的变量和方法的特性。

之前的学习中,我们使用构造函数定义了一个具有皮毛、身高和体重的猫科动物类,代码如下:

class FelidAnimal():
    def __init__(self,further,height,weight):
        self.further=further
        self.height=height
        self.weight=weight

然后我们还定义了一个具有姓名、年龄、毛皮、身高和体重的猫类,代码如下:

class Cat():
    def __init(self,name,age,further,height,weight):
        self.name=name
        self.age=age
        self.further=further
        self.height=height
        self.weight=weight

我们可以想到这样两件事:一是猫类和猫科动物类有是哪个共有的实例变量further,height和weight;而是猫类比猫科动物类多两个实例变量name和age。
对于这种关系,我们就可以定义父类和子类关系,用类的继承性来处理。抄写下面的代码,体会继承是怎样体现在构造方法中的。

class FelidAnimal():
    def __init__(self,further,height,weight):
        self.further=further
        self.height=height
        self.weight=weight
class Cat(FelidAnimal):
    def __init__(self,name,age,further,height,weight):
        super().__init__(further,height,weight)
        self.name=name
        self.age=age
tom_cat = Cat("Tom", 2, "black", 0.5, 2.0)
print("Tom的毛发颜色是%s" % tom_cat.further)
Tom的毛发颜色是black

在代码练习中,我们首先定义了FelidAnimal类,然后定义了Cat类,Cat类继承自FelidAnimal类。
Python规定super( )用来指代当前类的父类。因此在Cat类中,super( )指代就是父类FelidAnimal类
在Cat类的构造函数中,我们首先使用 super().init(futher, height, weight) 运行了父类的构造函数,对 self.further 、 self.height 和 self.weight 三个实例变量进行赋值;
然后使用两个赋值语句分别对 self.name 和 self.age 进行赋值。
讨论完了构造函数如何使用继承后,我们继续来看类变量、静态方法、实例方法和类方法怎么使用继承。
实际上,这种继承更加简单,用一句话说就是:子类自动获得类变量、静态方法、实例方法和类方法。
运行下面的代码,体会类变量、静态方法和实例方法的继承。老师会在代码练习之后给你讲解。

class FelidAnimal():
    leg_number=4
    def __init__(self,further,height,weight):
        self.further=further
        self.height=height
        self.weight=weight
    
    @staticmethod
    def change_weight(x):
        new_weight=x+0.1
        return new_weight
        
    def eat(self):
        print('正在吃东西')
        self.weight=self.change_weight(self.weight)
        print('体重增长了0.1,变成了%s'%self.weight)
class Cat(FelidAnimal):
    def __init__(self,name,age,further,height,weight):
        super().__init__(further,height,weight)
        self.name=name
        self.age=age

tom_cat = Cat('Tom',2,'black',0.5,2.0)
tom_cat.eat()
正在吃东西
体重增长了0.1,变成了2.1

在代码练习中,我们在猫科动物类里面定义了一个静态方法change_weight和实例方法eat,其中eat实例方法调用了change_weight静态方法。
当猫类继承猫科动物类的时候,它也自动获取了这些方法。我们使用猫类构造了一个实例tom_cat,这个实例也就能够使用实例方法eat了。
现在轮到你来,再在猫科动物类里添加一个类方法,起名为 variation (变异的英文单词),这个类方法能够实现把类变量 leg_number 变成3,然后让猫类继承这个类方法。通过构造一个实例,检验这种继承性。
补全下面的代码,使用实例调用继承的类方法variation.

class FelidAnimal():
    leg_number=4
    def __init__(self,further,height,weight):
        self.further = further
        self.height=height
        self.weight=weight
    
    @staticmethod
    def change_weight(x):
        new_weight=x+0.1
        return new_weight
    def eat(self):
        print('正在吃东西。。。。')
        self.weight=self.change_weight(self.weight)
        print('体重增长了0.1,变成了%s'%self.weight)
    @classmethod
    def variation(cls):
        cls.leg_number=3
        print('哦,发生了变异,腿的数量变成了%i'%cls.leg_number)

class Cat(FelidAnimal):
    def __init__(self,name,age,further,height,weight):
        super().__init__(further,height,weight)
        self.name=name
        self.age=age

tom_cat=Cat('Tom',2,'black',0.5,0.5)
tom_cat.eat()
print('原来腿的数量是%i'%tom_cat.leg_number)

##补全以下一行代码,调用继承的类方法
tom_cat.variation()
正在吃东西。。。。
体重增长了0.1,变成了0.6
原来腿的数量是4
哦,发生了变异,腿的数量变成了3

类的继承性使用起来十分方便。想象一下,如果又需要定义老虎类、豹类等,这些都是猫科动物类的子类,我们不需要在这些类里写一些变量和方法,只需要让它们继承猫科动物类就可以了。

多继承

所谓多继承:就是一个子类有多个父类。多继承是Python优于JAVA等面向对象语言的重要特征。

多继承是十分有必要的,因为现实中存在大量多继承的例子。例如,猫类不仅继承自猫科动物类,还继承自宠物类。

java等语言难以实现多继承,主要是因为继承会发生冲突,比如说如果猫科动物类中有eat方法,宠物类也有eat方法,这时候猫类在继承时就会发生冲突。

Python给出的方案是,当子类实例调用一个方法时,先从子类中查找(体现多态,稍后会讲解),如果子类中没有,则按照父类书写顺序从左到右查找,如果没有找到,再从父类的父类查找。。。依次类推。

为了减少你敲代码的负担,老师给你准备好了三个类:猫科动物类FelidAnimal,宠物类Pet,猫类Cat;其中猫类依次继承猫科动物类和宠物类。

成功运行下面的代码,观察tom猫调用eat函数后悔产生什么效果;然后修改代码,修改猫类的继承顺序,让其依次继承宠物类和猫科动物类。
提示:修改继承顺序后请注意修改父类(super())调用构造方法时的参数值。

class FelidAnimal():
    leg_number = 4
    def __init__(self, further, height, weight):
        self.further = further
        self.height = height
        self.weight = weight
    @staticmethod
    def change_weight(x):
        new_weight = x + 0.1
        return new_weight
    def eat(self):
        print("正在吃东西...")
        self.weight = self.change_weight(self.weight)
        print("体重增长了0.1,变成了%s" % self.weight)

class Pet():
    def __init__(self,name,further,height,weight):
        self.name=name
        self.further=further
        self.height=height
        self.weight=weight
    @staticmethod
    def change_weight(x):
         new_weight=x+0.3
         return new_weight
     
    def eat(self):
         print('正在吃豪华套餐....')
         self.weight=self.change_weight(self.weight)
         print('体重增长了0.3,变成了%s'%self.weight)

class Cat(Pet,FelidAnimal):
    def __init__(self,name,age,further,height,weight):
        super().__init__(name,further,height,weight)
        self.age=age

tom_cat=Cat('Tom',2,'black',0.5,2.0)
tom_cat.eat()
正在吃豪华套餐....
体重增长了0.3,变成了2.3

动手编写继承先继承FelidAnimal的eat方法。

思考:在上一条内容中老师代码部分的第29行 class Cat(Pet, FelidAnimal): 表示的是 Cat 类先继承Pet 类,再继承 FelidAnimal 类;如果把该行代码修改为 class Cat(FelidAnimal,Pet): , Cat 类先继承 FelidAnimal 类,再继承 Pet 类,现在请你判断,实例每次调用 eat 方法,实例变量self.weight实际变化值是多少?(找同学回答)

总结
在这里插入图片描述

类的多态性

重写

学完了类的继承性,有人会问,如果子类只是继承父类的特性,那定义子类还有什么意义呢,直接使用父类不就好了?
类的多态性完美地回答了这个问题。用一句话来总结就是:类的继承性保证了类的逻辑性和整洁性,类的多态性保证了类的丰富性。
前面讲过,多态性是指父类与子类具有不同变量和功能的特性。发生多态性要有两个前提条件:第一,多态性是发生在父类和子类之间;第二,多态性发生的基础是子类重写父类变量或者父类方法。
例如,猫科动物类有eat方法,它让实例的体重增加了0.1;如果我们重新在猫类里重新定义一个eat方法,那么在实例调用eat方法的时候,就会去调用猫类重写的eat方法,这就是多态性。

任务:方法重写,猫科动物类的eat方法时让体重增加0.1,在猫类中重写了eat方法,使体重增加了0.2.

class FelidAnimal():
    leg_number = 4
    def __init__(self,further,height,weight):
        self.further=further
        self.height=height
        self.weight=weight
    @staticmethod
    def change_weight(x):
        new_weight=x+0.1
        return new_weight
    
    def eat(self):
        print('正在吃东西。。。。')
        self.weight=self.change_weight(self.weight)
        print("体重增长了0.1,变成了%s"%self.weight)


class Cat(FelidAnimal):
    def __init__(self,name,age,further,height,weight):
        super().__init__(further,height,weight)
        self.name=name
        self.age=age
    @staticmethod
    def change_weight(x):
        new_weight=x+0.2
        return new_weight
    def eat(self):
        print('正在吃东西。。。')
        self.weight=self.change_weight(self.weight)
        print('体重增长了0.2,变成了%s'%self.weight)
tom_cat=Cat('Tom',2,'black',0.5,2.0)
tom_cat.eat()
正在吃东西。。。
体重增长了0.2,变成了2.2

在代码练习中,我们在FelidAnimal类中定义了eat方法,Cat 类继承了 FelidAnimal 类,如果不发生多态,Cat 实例 tom_cat 调用 eat 方法时应该调用的是 FelidAnimal 的 eat 方法;
现在我们在 Cat 类中也定义了 eat 方法,这就意味着子类 Cat 中的 eat 方法和父类 FelidAnimal 类中的 eat 方法不再是同一个,使用 tom_cat.eat 时调用的就是子类 Cat 中的 eat 方法。这就是类的多态性。
我们再来做一个多态性的练习。还记得object类的魔法方法__str__吗?现在我们在自定义的类中重写这个方法:
自定义一个学生类Student,在构造方法里面定义两个实例变量self.name和self.school,分别存储实例的姓名和学校名;
现在要求在print(学生实例)时打印出这个实例的姓名和学校名。

class Student(object):
    def __init__(self,name,school):
        self.name=name
        self.school=school
    def __str__(self):
        #定义打印的信息
        information='这是一个学生实例,姓名为%s,学校为%s'%(self.name,self.school)
        #返回打印的信息
        return information
tom_student=Student('Tom','工商学院')
print(tom_student)
这是一个学生实例,姓名为Tom,学校为工商学院

类型检查

想象一下我们再讨论一只猫咪属于什么类的时候会怎么说?没错,我们会说猫咪及时一个猫类,有一个猫科动物类。
在Python师姐中判断实例归属的时候也是这样。Python使用instance(s,c)来判断某个实例是否属于某个类,其中s是实例名称,c是类名称。如果s属于c,则函数返回True,否则,返回False。
我们尝试一下判断上面讲到的tom_cat是否既属于猫类,又属于猫科动物类。运行下面的代码,学习类的类型检查。

class FelidAnimal():
    leg_number = 4
    def __init__(self, further, height, weight):
        self.further = further
        self.height = height
        self.weight = weight
    @staticmethod
    def change_weight(x):
        new_weight = x + 0.1
        return new_weight
    def eat(self):
        print("正在吃东西...")
        self.weight = self.change_weight(self.weight)
        print("体重增长了0.1,变成了%s" % self.weight)
class Cat(FelidAnimal):
    def __init__(self, name, age, further, height, weight):
        super().__init__(further, height, weight)
        self.name = name
        self.age = age
tom_cat = Cat("Tom", 2, "black", 0.5, 2.0)
print(isinstance(tom_cat, Cat))
print(isinstance(tom_cat, FelidAnimal))
True
True

isinstance(tom_cat, Cat)和isinstance(tom_cat, FelidAnimal)返回的值都是True,说明tom_cat既属于猫类,又属于猫科动物类。

利用isinstance(s, c)判断实例s是否属于类c的操作叫做类型检查。还有一种比较常见的类型检查是issubclass(c1,c2),它能够用来判断类c1是否是类c2的子类。
现在,你来训练一下。 老师给你准备好了猫类和猫科动物类。你来补充代码,判断猫类是不是猫科动物类的子类。老师不再给你提供答案啦,如果有疑惑,可以在代码运行错误之后点击提示查看标准答案。

class FelidAnimal():
    leg_number = 4
    def __init__(self, further, height, weight):
        self.further = further
        self.height = height
        self.weight = weight
class Cat(FelidAnimal):
    def __init__(self, name, age, further, height, weight):
        super().__init__(further, height, weight)
        self.name = name
        self.age = age
# 补充一行代码,检查猫类是不是猫科动物的子类



总结
在这里插入图片描述

英雄归来

我们已经学完了Python面向对象编程所有的常用知识,现在我们利用这些知识来模拟网上很火的游戏王者荣耀。
为了实现起来简单一些,我们定义了英雄类作为父类,它的子类有法师类、射手类和坦克类。他们有两个基本的实例变量:姓名和经验值。
姓名实例变量是对这个英雄的标识,经验值实例变量可以用来计算其它实例变量。
这些实例变量是:经验值每增长100,等级增长一级;等级每增长一级,血量、物理攻击、物理护甲、魔法攻击和魔法护甲都有相应的增长。下图中总结了等级和其它实例变量的关系表。
在这里插入图片描述
根据这个关系表,我们可以总结出四个类的继承和多态的关系。
在这里插入图片描述

在课堂学习中老师分为三个步骤和你一起完成这个游戏:定义英雄类、定义三个子类和定义英雄类的实例方法。
在课后练习中,老师教会你如何指挥你的英雄去“狩猎”,提升自己的等级;老师还会给你留一些开放性的题目,让你提升这个游戏的完整性。

定义英雄类

我们首先定义英雄类,在定义构造方法时,我们应该有两个基本实例变量的参数:name和experience。
在构造方法中,等级level、血量health、物理攻击physical_attack、物理护甲physical_armor、魔法攻击magic_attack和麻烦护甲magic_armor可以根据等级来计算。
在定义实例方法的时候,我们首先使用pass占位符保证语法正确。
抄写下面的代码,到编辑器中,完成英雄类的基本框架。

class Hero():
    def __init__(self,name,experience):
        self.name=name
        self.experience=experience
        self.level=self.experience//100
        self.health=1000+(self.level*100)
        self.physical_attack=150+(self.level*20)
        self.physical_armor=50+(self.level*10)
        self.magic_attack=0
        self.magic_armor=50+(self.level*20)
        
    def attack(self,enemy):
        pass
    def level_up(self):
        pass
    def die(self):
        pass
    def hunt(self):
        pass
    def match_fight(self):
        pass

class Magician(Hero):
    pass
class Shooter(Hero):
    pass
class Tank(Hero):
    pass

在练习的过程中,你心里的代码可能和老师的有些出入,不用担心,只要你认为代码逻辑是对的,不妨先坚持自己的思路。写代码本来就没有标准答案,要记住,条条大路通罗马。
点击运行,发现什么都没有发生,这就对了。因为我们只是定义了类,还什么都没有实现。下面我们将利用这一系列代码练习来一起来回顾面向对象编程的知识。
使用class创建名为Hero的类,Hero命名遵循驼峰法;Hero后面的括号里什么都没加,意味着它默认继承了祖先类object。
定义构造方法,方法名是__init__,它是一个魔法实例方法,第一个参数为self,后面跟着其他参数。

英雄类只需要两个参数就可以,第一个是名称name,第二个是经验值experience,等级实例变量使用经验值计算得到,其它实例变量使用等级变量计算得到。
使用self.xxx分别对实例变量进行赋值,推荐self.后面的实例变量名称部分同参数名一样。
分别定义attack、level_up、die、hunt和match_fight五个实例方法,实例方法的第一个参数为self,由于还没有定义实例方法的具体功能,暂时使用pass占位符保证语法的正确性。

定义三个子类

现在定义法师类、射手类、坦克类三个子类。
查看上文图中英雄类和三个子类的继承、多态关系,我们发现法师类的多态体现在它重写了英雄类的魔法攻击变量;射手类重写了英雄类的物理攻击变量;坦克类重写了血量变量、物理护甲变量和魔法护甲变量。
在这里插入图片描述
根据关系表,完成三个子类的定义。法师的类名叫做Magician,射手的类名叫做Shooter,坦克的类名叫做Tank。

class Hero():
    def __init__(self, name, experience):
        self.name = name
        self.experience = experience
        self.level = self.experience // 100   #级别
        self.health = 1000 + (self.level*100)      #血量
        self.physical_attack = 150 + (self.level*20)   #物理攻击
        self.physical_armor = 50 + (self.level*10)   #物理护甲
        self.magic_armor = 50 + (self.level*20)  #魔法护甲
        self.magic_attack = 0   #模仿攻击
    def attack(self, enemy):
        pass
    def level_up(self, value):
        pass
    def die(self):
        pass
    def hunt(self):
        pass
    def match_fight(self):
        pass
# 定义法师类
class Magician(Hero):
    def __init__(self,name,experience):
        super().__init__(self,name,experience)
        #重写父类Hero的魔法攻击
        self.magic_attack=80+self.level*60


# 定义射手类
class Shooter(Hero):
    def __init__(self,name,experience):
        super().__init__(self,name,experience)
        #重写父类Hero的物理攻击
        self.physical_attack=200+slef.level*50


# 定义坦克类
class Tank(Hero):
    def __init__(self,name,experience):
        super().__init__(self,name,experience)
        #重写父类的血量和物理护甲和魔法护甲
        self.health=2000+self.level*200
        self.physical_armor=80+slef.level*20
        self.magic_armor=80+(self.level*300)

英雄的实例方法

现在我们给英雄类写实例方法。首先定义最基本的三个实例方法:攻击、升级和死亡。
具体方法是:
攻击方法有一个参数叫做敌人(enemy),攻击应该有两种方式,一种是物理攻击,一种是魔法攻击,攻击的伤害值是实例的攻击力减去敌人的护甲。
升级有一个叫做经验值(value)的参数,实例的经验值做相应的增长,等级根据经验值变化。
等级的计算方式是,每增长100经验值,等级加一。如果等级增加了,每升一级,相应的血量、物理攻击、物理护甲、魔法攻击和魔法护甲增加值如前面表格定义。
死亡方法是,你的经验减100,等级减一,相应的血量、物理攻击、物理护甲、魔法攻击和模仿护甲变化如前面表格定义。

class Hero():
    def __init__(self, name, experience):
        self.name = name
        self.experience = experience
        self.level = self.experience // 100
        self.health = 1000 + (self.level*100)
        self.physical_attack = 150 + (self.level*20)
        self.physical_armor = 50 + (self.level*10)
        self.magic_armor = 50 + (self.level*20)
        self.magic_attack = 0
    def attack(self, enemy):
        physical_hurt = 0 if self.physical_attack < enemy.physical_armor else (self.physical_attack - enemy.physical_armor)
        magic_hurt = 0 if self.magic_attack < enemy.magic_armor else (self.magic_attack - enemy.magic_armor)
        enemy.health = enemy.health - physical_hurt - magic_hurt
        print("%s攻击了%s,一共造成%i点伤害。"% (self.name, enemy.name, (physical_hurt + magic_hurt)))
    def level_up(self, value):
        self.experience = self.experience + value
        now_level = self.experience // 100
        if now_level > self.level:
            print("恭喜你升级了,现在你的级别是%i。" % now_level)
        self.__init__(self.name, self.experience)
    def die(self):
        self.experience = self.experience - 100
        self.__init__(self.name, self.experience)
        print("你掉了一级,现在的级别是%i" % self.level)
    def hunt(self):
        pass
    def match_fight(self):
        pass
class Magician(Hero):
    def __init__(self, name,experience):
        super().__init__(name, experience)
        self.magic_attack = 80 + (self.level*60)
class Shooter(Hero):
    def __init__(self, name, experience):
        super().__init__(name, experience)
        self.physical_attack = 200 + (self.level*50)
class Tank(Hero):
    def __init__(self, name,experience):
        super().__init__(name, experience)
        self.health = 2000 + (self.level*200)
        self.physical_armor = 80 + (self.level*20)
        self.magic_armor = 80 + (self.level*30)

我们逐个方法分析,对于攻击方法attack,需要传入一个enemy实例。接着,使用if的三元表达式判断实例的攻击力是否小于敌人的护甲。if的三元表达式是逻辑判断的高级用法,它的语法规则如下。
在这里插入图片描述
如果攻击力小于敌人的护甲,则相应的伤害是0,否则 伤害=攻击力-敌人护甲。当然我们也可以使用多个判断语句实现相同的功能。
第二个方法:升级方法level_up。
需要传入一个value参数,代表经验的增长值,然后把value加到实例变量self.experience上;接着根据当前的经验值计算等级,如果等级增长了,打印出消息进行提示;最后,使用构造方法重新初始化实例。
第三个方法,死亡方法die。经验值减少100,然后使用构造方法重新初始化实例,打印出提示信息。
到现在,我们已经做出一个初据规模的游戏场景了,但是要实现对战,我们还有很长的路要走。在本周的课后练习题中,老师会带你进一步地完成这个游戏,让你体验一把玩“自己编写的游戏”的成就感。
需求:
现在你来构造一个射手实例xiaoxiang,定义实例变量值为name = “Draven”, experience = 0;构造一个法师实例daxiang,定义实例变量值为name = “Ryze”,experience = 100。
依次完成以下操作:指挥Draven去攻击Ryze,然后查看Ryze生命值的变化;让Ryze升两级;让Ryze死亡一次。

class Hero():
    def __init__(self, name, experience):
        self.name = name
        self.experience = experience
        self.level = self.experience // 100
        self.health = 1000 + (self.level*100)
        self.physical_attack = 150 + (self.level*20)
        self.physical_armor = 50 + (self.level*10)
        self.magic_armor = 50 + (self.level*20)
        self.magic_attack = 0
    def attack(self, enemy):
        physical_hurt = 0 if self.physical_attack < enemy.physical_armor else (self.physical_attack - enemy.physical_armor)
        magic_hurt = 0 if self.magic_attack < enemy.magic_armor else (self.magic_attack - enemy.magic_armor)
        enemy.health = enemy.health - physical_hurt - magic_hurt
        print("%s攻击了%s,一共造成%i点伤害。"% (self.name, enemy.name, (physical_hurt + magic_hurt)))
    def level_up(self, value):
        self.experience = self.experience + value
        now_level = self.experience // 100
        if now_level > self.level:
            print("恭喜你升级了,现在你的级别是%i。" % now_level)
        self.__init__(self.name, self.experience)
    def die(self):
        self.experience = self.experience - 100
        self.__init__(self.name, self.experience)
        print("你掉了一级,现在的级别是%i" % self.level)
    def hunt(self):
        pass
    def match_fight(self):
        pass
class Magician(Hero):
    def __init__(self, name,experience):
        super().__init__(name, experience)
        self.magic_attack = 80 + (self.level*60)
class Shooter(Hero):
    def __init__(self, name, experience):
        super().__init__(name, experience)
        self.physical_attack = 200 + (self.level*50)
class Tank(Hero):
    def __init__(self, name,experience):
        super().__init__(name, experience)
        self.health = 2000 + (self.level*200)
        self.physical_armor = 80 + (self.level*20)
        self.magic_armor = 80 + (self.level*30)
# 构造一个射手实例,name为Draven,经验是0
Draven=Shooter("Draven",0)
# 构造一个法师实例,name为Ryze,经验是100
Ryze=Magician("Ryze",100)
# 打印出Draven的姓名和血量
print(Draven.name)
print(Draven.health)
# 指挥Draven去攻击Ryze
xiaoxiang.attack(Ryze)
# 打印出daxiang现在的姓名和血量
print(Ryze.name)
print(Ryze.health)
# 打印出Ryze现在的等级
print(Ryze.level)
# 增加Ryze200的经验
Ryze.level_up(200)
# 打印出Ryze姓名和现在的等级
print(Ryze.name)
print(Ryze.health)
# 让Ryze死亡一次
Ryze.die()
Draven
1000
Draven攻击了Ryze,一共造成140点伤害。
Ryze
960
1
恭喜你升级了,现在你的级别是3。
Ryze
1300
你掉了一级,现在的级别是2

关于王者荣耀的游戏我们先进行到这里,细心的同学们一定发现了,老师还预留了几个实例方法没有实现。在本关的练习题中,老师会带你实现一些方法;老师还会为你预留一些“玩法”的方向,让你在课后一点点探索。

总结

在这里插入图片描述

课后练习

练习1

在我们定义的王者荣耀英雄类中,我们定义了hunt狩猎/打野方法,但是当前使用的是pass占位符。现在你来完成hunt方法。
完成之后,记得构造一个实例然后通过狩猎来提高你的等级吧!
题目要求
定义课堂学习中的hunt实例方法,要求如下:
hunt方法传入一个参数master,表示野怪类的实例;hunt方法实现英雄实例跟野怪实例的对战,直到双方有一方血量小于等于0;如果英雄实例获胜,他的经验值提高80,如果英雄输了,他调用die( )实例方法。
动手操作
老师已经给你准备好了课堂练习中定义好的类和方法,你只需要完成hunt方法就可以了。每一个狩猎过程英雄实例self要跟猎物master互相攻击,直到有一方死亡。如果self死亡,调用die( )方法;如果master死亡,调用level_up(80)。
hunt方法的逻辑是这样的:

  • 1、设置回合的初始值为1,每次循环时打印出回合信息,回合结束回合数加1;
  • 2、调用self.attack(master)让实例攻击猎物,判断猎物的血量是否小于等于0,如果小于等于0,打印狩猎成功的信息,并且调用self.level_up(80),并且退出循环;
  • 3、调用master.attack(self)让猎物攻击实例,判断实例的血量是否小于等于0,如果小于等于0,打印狩猎失败的信息,并且调用self.die( ),并且退出循环;

作业2

正所谓知己知彼,百战不殆。在狩猎之前,你应该充分了解你和猎物的实力。如果觉得能够战胜猎物,就投入战斗;如果觉得猎物太强大,那就果断逃跑——毕竟好汉不吃眼前亏。
现在请你给hunt方法添加打印实例和猎物信息的功能,并给用户提供战斗还是逃跑的选择。
题目要求
在狩猎开始之前,分别打印出你和猎物的基本信息,判断是否能打得过;如果觉得能够战胜对方,就投入战斗;如果觉得不能战胜,就输入run逃跑。
动手操作
相信你的心里已经有思路了,老师给你提醒以下几点:

  • 1、使用三引号配合%字符串格式化让输出的基本信息更加美观;
  • 2、使用input( )函数让你选择是否逃跑,判断如果输入的是run,则结束战斗;
  • 3、如果用户输入的是run,则使用return结束狩猎方法。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值