python学习第16节:类的继承、多态特性

发布时间:2020/07/15 作者:天马行空 阅读(1084)

一、类的继承性

1.1、object类
当我们定义一个类,使用类似class ABC()这样的形式时,父类括号中看起来是空的,但实际上,此时的ABC类并不是没有父类,而是默认继承Python的根类:object类。
因此,object类可以看做是Python所有类的“祖先”。

Python的内置方法dir( )
dir( )的语法规则是dir(o),其中o是一个类,dir(o)返回的是类o所有的方法。

class FelidAnimal():
    pass
print("FelidAnimal的变量有{}" .format(FelidAnimal.__dict__))
print("======")
print("FelidAnimal的方法有{}" .format(dir(FelidAnimal)))

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

1.2、继承
类的继承性是指当前类能够保留父类的变量和方法的特性。
使用构造函数定义了一个具有皮毛、身高和体重的猫科动物类,代码如下。

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


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

class Cat():
    def __init__(self, name, age, fur, height, weight):
        self.name = name
        self.age = age
        self.fur = fur
        self.height = height
        self.weight = weight


可以想到这样两件事情:一是猫类和猫科动物类有三个共有的实例变量fur、height和weight;二是猫类比猫科动物类多两个实例变量name和age。
对于这种关系,我们就可以定义父类和子类关系,用类的继承性来处理。

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


首先定义了FelidAnimal类,然后定义了Cat类,Cat类继承自FelidAnimal类。
这时,称FelidAnimal是Cat的父类,Cat是FelidAnimal的子类。
Python规定super( )用来指代当前类的父类。因此在Cat类中,super( )就是指父类FelidAnimal。

受保护的属性
定义属性时,在属性名前面放置一个下划线,就表示这个属性是受保护属性。比如将__fur改成_fur,它就成为了一个受保护的属性。
受保护属性的特点介于私有属性和公有属性之间:
它不能在类外直接访问到。
它可以被子类继承。

子类自动获得父类的公有、受保护的类变量、静态方法、实例方法和类方法。对于父类的私有属性及方法,子类不能继承。

class FelidAnimal():
    leg_number = 4
    def __init__(self, fur, height, weight):
        self.__fur = fur
        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,变成了{}".format(self.__weight))
        
class Cat(FelidAnimal):
    def __init__(self, name, age, fur, height, weight):
        super().__init__(fur, height, weight)
        self.__name = name
        self.__age = age
tom_cat = Cat("Tom", 2, "black", 0.5, 2.0)
tom_cat.eat()


在猫科动物类里面定义了一个静态方法__change_weight和实例方法eat,其中eat实例方法调用了__change_weight静态方法。
静态方法__change_weight是个私有方法,用于辅助完成增加实例的体重(__weight)的功能。它不能被外界直接调用,而只有当吃了东西(调用eat方法)之后,体重才会增加。
当猫类继承猫科动物类的时候,它也自动获取了eat()方法。我们使用猫类构造了一个实例tom_cat,这个实例也就能够调用实例方法eat()了。

二、类的多态性

2.1、重写
多态性是指子类与父类具有不同变量和功能的特性。发生多态性要有两个前提条件:第一,多态性是发生在父类和子类之间;第二,多态性发生的基础是子类重写父类变量或者父类方法。

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


在FelidAnimal类中定义了change_weight方法。Cat类继承了FelidAnimal类,如果不发生多态,Cat实例tom_cat调用eat方法时会最终触发FelidAnimal的change_weight方法;
现在我们在Cat类中也定义了change_weight方法,这就意味着子类Cat中的change_weight方法和父类FelidAnimal类中的change_weight方法不再是同一个,使用tom_cat.eat时最终触发的就是子类Cat中的change_weight方法。这就是类的多态性。

练习:重写__str__方法

class Student():
    def __init__(self, name, school):
        self.__name = name
        self.__school = school
    def __str__(self):
        # 定义打印的信息
        information = "这是一个学生实例,姓名为{},学校为{}".format(self.__name, self.__school)
        # 返回打印的信息
        return information
tom_student = Student("Tom", "小象学院")
print(tom_student)


class Student()的声明中,括号里是空的,表示它默认继承Python的基类object。通过def __str__(self)这段代码,子类重写了object类中已经定义的这个魔法方法。
当Student类的实例tom_student被传做print函数的参数,进行信息打印的时候,print函数其实就是调用了传给它的这个参数,也就是tom_student的__str__()方法,打印出__str__()方法return的字符串内容。于是,我们就看到了自己在__str__()中定义的字符串内容被输出了。

2.2、类型检查
在Python中判断实例归属的时候也是这样。Python使用isinstance(s, c)来判断某个实例是否属于某个类,其中s是实例名称,c是类名称。如果s属于c,则函数返回True,否则,返回False。

class FelidAnimal():
    leg_number = 4
    def __init__(self, fur, height, weight):
        self.__fur = fur
        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,变成了{}".format(self.__weight))
class Cat(FelidAnimal):
    def __init__(self, name, age, fur, height, weight):
        super().__init__(fur, 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))


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, fur, height, weight):
        self.__fur = fur
        self.__height = height
        self.__weight = weight
class Cat(FelidAnimal):
    def __init__(self, name, age, fur, height, weight):
        super().__init__(fur, height, weight)
        self.__name = name
        self.__age = age
print(issubclass(Cat,FelidAnimal))

图片.png


三、英雄归来
现在利用这些知识来模拟网上很火的游戏王者荣耀。
为了实现起来简单一些,我们定义了英雄类作为父类,它的子类有法师类、射手类和坦克类。他们有两个基本的实例变量:姓名和经验值。

图片.png


图片.png


3.1、定义英雄类

首先定义英雄类,在定义构造方法时,我们应该有两个基本实例变量的参数:姓名(name)和经验值(experience)。

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, value):
        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


3.1、定义三个子类

现在定义法师类、射手类、坦克类三个Hero的子类。

法师类需要重写英雄类的魔法攻击属性(magic_attack),基础值修改为80,每level增长60(当前魔法攻击即为80 + level * 60)。
射手类需要重写英雄类的物理攻击属性(physical_attack),基础值修改为200,每level增长50。
坦克类需要重写英雄类的血量(health,基础值修改为2000,每level增长200)、物理护甲(physical_armor,基础值修改为80,每level增长20)和魔法护甲(magic_armor,基础值修改为80,每level增长30)。

图片.png

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__(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)

我们分别定义Magician类、Shooter类和Tank类。其中,在Magician类中重写了魔法攻击实例变量;在Shooter类里面重写了物理攻击力实例变量;在Tank类里面重写了血量、物理护甲和魔法护甲三个实例变量。


3.3、英雄的实例方法

class Hero():
    def __init__(self, name, experience):
        '''
        实例构造方法
        传入参数
            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):
        '''
        攻击方法
        传入参数
            enemy:被攻击的实例
        主要功能
            分别计算物理伤害及魔法伤害,并将enemy的血量降低相应的伤害值。
        '''
        # 物理伤害等于当前实例物理攻击能力减敌人物理防御能力
        # 如果攻击能力低于防御能力,物理伤害为0
        physical_hurt = 0 if self.physical_attack < enemy.physical_armor else (self.physical_attack - enemy.physical_armor)
        # 魔法伤害等于当前实例魔法攻击能力减敌人魔法防御能力
        # 如果攻击能力低于防御能力,魔法伤害为0
        magic_hurt = 0 if self.magic_attack < enemy.magic_armor else (self.magic_attack - enemy.magic_armor)
        # 根据伤害能力,设置enemy的血量为原血量减掉伤害值
        enemy.health = enemy.health - physical_hurt - magic_hurt
        print("{}攻击了{},一共造成{}点伤害。".format(self.name, enemy.name, (physical_hurt + magic_hurt)))
    def level_up(self, value):
        '''
        升级方法
        传入参数
            value:增加的经验值
        主要功能
            将当前实例的经验值增加value,并根据新的经验值计算对应的级别。
            如果此次经验值增加导致了升级,调用实例构造方法重新计算新的级别、血量及各攻击和防御能力。
        '''
        # 增加经验值
        self.experience = self.experience + value
        # 根据新的经验值计算级别
        now_level = self.experience // 100
        # 如果新计算的级别比原来的级别高,打印恭喜信息,并调用实例构造方法重新计算各属性
        if now_level > self.level:
            print("恭喜{}升级了,现在你的级别是{}。".format(self.name, now_level))
            self.__init__(self.name, self.experience)
    def die(self):
        '''
        死亡方法
        主要功能
            经验值减100,并调用实例构造方法,根据新的经验值重新计算其它各属性值。
        '''
        self.experience = self.experience - 100
        self.__init__(self.name, self.experience)
        print("{}掉了一级,现在的级别是{}".format(self.name, self.level))
    def hunt(self):
        pass
    def match_fight(self):
        pass

class Magician(Hero):
    '''
    法师类
    '''
    def __init__(self, name,experience):
        '''
        调用父类构造方法后,重写magic_attack,
        法师类魔法攻击基础值为80,每级别上涨60。
        '''
        super().__init__(name, experience)
        self.magic_attack = 80 + (self.level*60)

class Shooter(Hero):
    '''
    射手类
    '''
    def __init__(self, name, experience):
        '''
        调用父类构造方法后,重写physical_attack,
        射手类物理攻击基础值为200,每级别上涨50。
        '''
        super().__init__(name, experience)
        self.physical_attack = 200 + (self.level*50)

class Tank(Hero):
    '''
    坦克类
    '''
    def __init__(self, name,experience):
        '''
        调用父类构造方法后,重写health、physical_armor和magic_armor。
        '''
        super().__init__(name, experience)
        self.health = 2000 + (self.level*200)
        self.physical_armor = 80 + (self.level*20)
        self.magic_armor = 80 + (self.level*30)

# 构造一个射手实例xiaoxiang,name为Draven,经验是0
xiaoxiang = Shooter('Draven',0)

# 构造一个法师实例daxiang,name为Ryze,经验是100
daxiang = Magician('Ryze',100)

# 打印出daxiang的姓名和血量
print('daxiang的姓名是{}血量是{}'.format(daxiang.name,daxiang.health))

# 指挥xiaoxiang去攻击daxiang
xiaoxiang.attack(daxiang)

# 打印出daxiang现在的姓名和血量
print('daxiang的姓名是{}血量是{}'.format(daxiang.name,daxiang.health))

# 打印出daxiang现在的等级
print('daxiang现在的等级是{}'.format(daxiang.level))

# 为daxiang增加200的经验
daxiang.level_up(200)

# 打印出daxiang姓名和现在的等级
print('daxiang现在的等级是{}'.format(daxiang.level))

# 让daxiang死亡一次
daxiang.die()

攻击方法:如果攻击力小于敌人的护甲,则相应的伤害是0,否则伤害=攻击力-敌人护甲。

升级方法:需要传入一个value参数,代表经验的增长值,然后把value加到实例变量self.experience上。接着根据当前的经验值计算等级,如果等级增长了,打印出消息进行提示,并且调用构造方法,根据新的经验值,计算血量、攻击力等其它属性。

死亡方法:经验值减少100,然后调用实例的构造方法,根据新的经验值重新计算实例的级别、血量、攻击力等其它属性。


if的三元表达式

图片.png


总结
object类:Python自定义的祖先类,所有类默认继承object类。
继承:子类从父类继承属性和方法。
重写:实现多态性的重要手段,指的是子类重新定义变量和方法。
图片.png


练习:指挥daxiang去狩猎,猎物为xiaoxiang

class Hero():
    def __init__(self, name, experience):
        '''
        实例构造方法
        传入参数
            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):
        '''
        攻击方法
        传入参数
            enemy:被攻击的实例
        主要功能
            分别计算物理伤害及魔法伤害,并将enemy的血量降低相应的伤害值。
        '''
        # 物理伤害等于当前实例物理攻击能力减敌人物理防御能力
        # 如果攻击能力低于防御能力,物理伤害为0
        physical_hurt = 0 if self.physical_attack < enemy.physical_armor else (self.physical_attack - enemy.physical_armor)
        # 魔法伤害等于当前实例魔法攻击能力减敌人魔法防御能力
        # 如果攻击能力低于防御能力,魔法伤害为0
        magic_hurt = 0 if self.magic_attack < enemy.magic_armor else (self.magic_attack - enemy.magic_armor)
        # 根据伤害能力,设置enemy的血量为原血量减掉伤害值
        enemy.health = enemy.health - physical_hurt - magic_hurt
        print("{}攻击了{},一共造成{}点伤害。".format(self.name, enemy.name, (physical_hurt + magic_hurt)))
    def level_up(self, value):
        '''
        升级方法
        传入参数
            value:增加的经验值
        主要功能
            将当前实例的经验值增加value,并根据新的经验值计算对应的级别。
            如果此次经验值增加导致了升级,调用实例构造方法重新计算新的级别、血量及各攻击和防御能力。
        '''
        # 增加经验值
        self.experience = self.experience + value
        # 根据新的经验值计算级别
        now_level = self.experience // 100
        # 如果新计算的级别比原来的级别高,打印恭喜信息,并调用实例构造方法重新计算各属性
        if now_level > self.level:
            print("恭喜{}升级了,现在你的级别是{}。".format(self.name, now_level))
            self.__init__(self.name, self.experience)
    def die(self):
        '''
        死亡方法
        主要功能
            经验值减100,并调用实例构造方法,根据新的经验值重新计算其它各属性值。
        '''
        self.experience = self.experience - 100
        self.__init__(self.name, self.experience)
        print("{}掉了一级,现在的级别是{}".format(self.name, self.level))
    def hunt(self, monster):
        huihe = 1
        while True:
            print('第{}回合'.format(huihe))
            
            self.attack(monster)
            if monster.health<=0:
                self.level_up(80)
                break
            
            monster.attack(self)
            if self.health<=0:
                self.die()
                break
            
            huihe+=1
            
    def match_fight(self):
        pass
class Magician(Hero):
    '''
    法师类
    '''
    def __init__(self, name,experience):
        '''
        调用父类构造方法后,重写magic_attack,
        法师类魔法攻击基础值为80,每级别上涨60。
        '''
        super().__init__(name, experience)
        self.magic_attack = 80 + (self.level*60)
class Shooter(Hero):
    '''
    射手类
    '''
    def __init__(self, name, experience):
        '''
        调用父类构造方法后,重写physical_attack,
        射手类物理攻击基础值为200,每级别上涨50。
        '''
        super().__init__(name, experience)
        self.physical_attack = 200 + (self.level*50)
class Tank(Hero):
    '''
    坦克类
    '''
    def __init__(self, name,experience):
        '''
        调用父类构造方法后,重写health、physical_armor和magic_armor。
        '''
        super().__init__(name, experience)
        self.health = 2000 + (self.level*200)
        self.physical_armor = 80 + (self.level*20)
        self.magic_armor = 80 + (self.level*30)
xiaoxiang = Shooter("Draven", 1000)
daxiang = Magician("Ryze", 100)
# 指挥daxiang去狩猎,猎物为xiaoxiang
daxiang.hunt(xiaoxiang)


关键字python python教程