发布时间:2020/07/15 作者:天马行空 阅读(1495)
一、类的继承性
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))
三、英雄归来
现在利用这些知识来模拟网上很火的游戏王者荣耀。
为了实现起来简单一些,我们定义了英雄类作为父类,它的子类有法师类、射手类和坦克类。他们有两个基本的实例变量:姓名和经验值。
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)。
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的三元表达式
总结
object类:Python自定义的祖先类,所有类默认继承object类。
继承:子类从父类继承属性和方法。
重写:实现多态性的重要手段,指的是子类重新定义变量和方法。
练习:指挥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)