Python学习日记10

Day.10

2020.02.28
今天同样是继续学习python的面向对象编程,内容主要是面向对象编程的继承和多态。这里先简单说一下概念,继承就像是儿子继承爸爸妈妈的长相一样,子类将会继承父类的属性和方法,当然,也可以添加子类自己特殊的属性和方法,也可以改写父类的方法,多个不同的子类继承同一父类,就是多态的表现。

1.面向对象进阶

i.装饰器@property

首先是装饰器@property。在上文提到,python类中的属性存在访问权限的问题,在要保护的属性名称前面加单一下划线是一种方法,虽然比较方便但是其实并不安全。如果想要对属性的访问既方便又安全,就可以使用@property包装器来包装getter和setter方法。
其中,getter方法用于将属性设为只读,而setter方法用于将属性设为可读写。使用方法如以下实例所示:

class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

很显然,name是只读属性,而age是可以进行读写操作的(虽然age定义了getter方法,但是同时也定义了setter方法,可以对其进行外来参数的赋值)。

ii.__slots__魔法

其次是魔法__slots__。因为python是一门动态的语言,它允许在程序运行时,给对象绑定新的属性或方法,或者给对象解绑一些属性和方法,所以在这个过程中,我们遇到的问题是,如何限定自定义对象只能绑定某些属性,那么这个时候就需要用__slots__。需要注意的是,__slots__只对当前类的对象有效,而对子类的对象无效。具体实现方法如下:

# 限定Person对象只能绑定_name, _age和_gender属性
    __slots__ = ('_name', '_age', '_gender')

iii.静态方法和类方法

第三个是静态方法和类方法。首先我列个表格总结一下实例方法、静态方法和类方法的区别:

方法定义调用
实例方法第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传的属性和方法)只能由实例对象调用
类方法使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递的属性和方法(不能传实例的属性和方法)实例对象和类对象都可以调用
静态方法使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法实例对象和类对象都可以调用

①类方法:原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。
②静态方法:静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护(比如骆昊百天项目中的例子:判断是否能创建三角形类对象的函数Triangle.is_valid(a, b, c),这是一个单纯的函数,Triangle是类名,并不是任何类对象或实例对象)。
关于这三种方法的详细比较和解释,我认为有一篇博文讲的是非常到位的:
Python实例方法、类方法、静态方法的区别与作用

iv.类之间的关系

虽然类和类之间在语法上只有继承,但是类与类之间可以分为三种关系:

  • is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
  • has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
  • use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
    如果以上的内容不好理解的话,可以看一下下面这张图:
    类与类之间的关系图
    这张图通过具体的例子把三种关系都写了出来:司机Driver继承于人类Person的大类,司机Driver类又和驾照License类关联,但是要注意的是,司机和驾照是同级别的关系,比如上文所说的部门和员工。另外,汽车Vehicle和引擎Engine也是关联关系,但这两者是整体和部分的关系,所以也叫聚合。司机需要开车,因此司机和汽车的关系是依赖关系。同样的,汽车Vehicle大类也可以继承出普通车子Car和卡车Truck的小类。
    继承和多态就不再多说,唯一需要注意的一点是:多继承的时候,如果父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索(即方法在子类中未找到时,从左到右查找父类中是否包含方法)。

2.综合案例练习

GitHub上在这一节的最后是三个综合案例,主要是灵活运用类的继承和多态,因为网站上直接有代码给出,我就没有重复编写这三个案例。但是为了达到练习的效果,我自己编写了一个回合制打怪的实例:

#回合制打怪:
#作者:囷囷
import random


class Role:
    name = ''
    hp = 500
    mp = 500
    attack = 100
    defend = 50
    damage = 0
    # 伤害计算公式: 伤害=自己攻击力-对手防御力

    def __init__(self, name, hp, mp, attack, defend):
        self.name = name
        self.hp = hp
        self.mp = mp
        self.attack = attack
        self.defend = defend
    # 普通攻击:

    def AtkTo(self, other):
        self.damage = self.attack - other.defend
        other.hp -= self.damage


class Player(Role):
    exp = 0
    level = 1
    skill = ['普通攻击']
    SkillDamage = 0
    MpIsEnough = 1

    def __init__(self, name, hp, mp, attack, defend, exp, level, SkillDamage):
        Role.__init__(self, name, hp, mp, attack, defend)
        self.exp = exp
        self.level = level
        self.SkillDamage = SkillDamage
    # 技能攻击:

    def SkillsTo(self, other):
        self.SkillDamage = self.SkillDamage - other.defend
        other.hp -= self.SkillDamage
    # 选择技能:

    def ChooseYourSkills(self, x):
        if self.skill[x] == '肉蛋葱鸡':
            if self.mp >= 90:
                self.MpIsEnough = 1
                self.mp -= 90
                self.SkillDamage = 270
            else:
                self.MpIsEnough = 0
                print('对不起!您的蓝量不足,无法再释放技能!')
        elif self.skill[x] == '我夹':
            if self.mp >= 50:
                self.MpIsEnough = 1
                self.mp -= 50
                self.SkillDamage = 90
            else:
                self.MpIsEnough = 0
                print('对不起!您的蓝量不足,无法再释放技能!')
        elif self.skill[x] == '起飞':
            if self.mp >= 70:
                self.MpIsEnough = 1
                self.mp -= 70
                self.SkillDamage = 200
            else:
                self.MpIsEnough = 0
                print('对不起!您的蓝量不足,无法再释放技能!')
        elif self.skill[x] == '治疗术':
            if self.mp >= 10:
                self.MpIsEnough = 1
                self.mp -= 10
                self.SkillDamage = 0
                self.hp += 100
            else:
                self.MpIsEnough = 0
                print('对不起!您的蓝量不足,无法再释放技能!')
        else:
            if self.mp >= 100:
                self.MpIsEnough = 1
                self.mp -= 100
                self.SkillDamage = 400
            else:
                self.MpIsEnough = 0
                print('对不起!您的蓝量不足,无法再释放技能!')


class Monster(Role):
    SkillDamage = 0
    skill = []
    MpIsEnough = 1
    TempSkill = ''

    def __init__(self, name, skill, hp, mp, attack, defend, SkillDamage):
        Role.__init__(self, name, hp, mp, attack, defend)
        self.SkillDamage = SkillDamage
        self.skill = skill

    def MonsterSkillsTo(self, other):
        self.SkillDamage = self.SkillDamage - other.defend
        if self.SkillDamage <= 0:
            self.SkillDamage = 0
            other.hp -= self.SkillDamage
        else:
            other.hp -= self.SkillDamage

    def ChooseMonsterSkills(self, other):
        self.TempSkill = random.choice(self.skill)
        if self.TempSkill == '普通攻击':
            self.SkillDamage = self.attack - other.defend
        elif self.TempSkill == '车轮滚滚':
            self.SkillDamage = 270
        elif self.TempSkill == '我夹':
            self.SkillDamage = 90
        elif self.TempSkill == '大地震击':
            self.SkillDamage = 200
        elif self.TempSkill == '势不可挡':
            self.SkillDamage = 400
        elif self.TempSkill == '疏通肠道':
            self.SkillDamage = 280
        elif self.TempSkill == '泰坦之怒':
            self.SkillDamage = 70
        elif self.TempSkill == '暗流涌动':
            self.SkillDamage = 175
        elif self.TempSkill == '深海葱鸡':
            self.SkillDamage = 80
        elif self.TempSkill == '斩钢闪':
            self.SkillDamage = 120
        elif self.TempSkill == '风之障壁':
            self.SkillDamage = 0
        elif self.TempSkill == '踏前斩':
            self.SkillDamage = 100
        else:
            self.SkillDamage = 400


def main():
    NotInRound = 1
    Player1 = Player('朱一飞', 1000, 1000, 100, 100, 0, 1, 0)
    Monster1 = Monster('墨菲特', ['普通攻击', '车轮滚滚', '我夹', '大地震击', '势不可挡'], 1000, 0, 50, 10, 0)
    Monster2 = Monster('深海泰坦', ['普通攻击', '疏通肠道', '泰坦之怒', '暗流涌动', '深海葱鸡'], 2000, 0, 10, 50, 0)
    Monster3 = Monster('亚索', ['普通攻击', '斩钢闪', '风之障壁', '踏前斩', '狂风绝息斩'], 200, 0, 200, 10, 0)
    MonsterList = [Monster1, Monster2, Monster3]
    round = 1
    datalist = ['肉蛋葱鸡', '我夹', '起飞', '千层饼', '治疗术']
    y = int(input('按1进入游戏,按其他键退出游戏:'))
    while True:
        if y == 1 and Player1.hp > 0:
            if NotInRound == 1:
                NotInRound = 0
                TempMonster = random.choice(MonsterList)
                if TempMonster.name == '墨菲特':
                    TempMonster.hp = 1000
                    TempMonster.mp = 0
                    TempMonster.attack = 50
                    TempMonster.defend = 10
                if TempMonster.name == '深海泰坦':
                    TempMonster.hp = 2000
                    TempMonster.mp = 0
                    TempMonster.attack = 10
                    TempMonster.defend = 50
                if TempMonster.name == '亚索':
                    TempMonster.hp = 200
                    TempMonster.mp = 0
                    TempMonster.attack = 200
                    TempMonster.defend = 10
                print('遭遇怪物%s!' % TempMonster.name)
                while TempMonster.hp > 0 and Player1.hp > 0:
                    print('----------信息:----------')
                    print('玩家%s:' % Player1.name)
                    print('HP:%d,MP:%d,EXP:%d,攻击力:%d,防御力:%d,等级:%d' % (
                    Player1.hp, Player1.mp, Player1.exp, Player1.attack, Player1.defend, Player1.level))
                    print('怪物%s:' % TempMonster.name)
                    print('HP:%d,MP:%d,攻击力:%d,防御力:%d' % (
                    TempMonster.hp, TempMonster.mp, TempMonster.attack, TempMonster.defend))
                    print('----------第%d回合----------' % round)
                    print('技能列表:')
                    for m in range(0, len(Player1.skill)):
                        print('%d:%s ' % (m, Player1.skill[m]))
                    x = int(input('请按技能列表对应的数字键进行操作:'))
                    if x == 0:
                        Player1.AtkTo(TempMonster)
                        TempMonster.ChooseMonsterSkills(Player1)
                        TempMonster.MonsterSkillsTo(Player1)
                        print('玩家%s使用了 %s 对怪物%s造成了%d点伤害!' % (
                        Player1.name, Player1.skill[x], TempMonster.name, Player1.damage))
                        print('怪物%s使用了 %s 对玩家%s造成了%d点伤害!' % (
                        TempMonster.name, TempMonster.TempSkill, Player1.name, TempMonster.SkillDamage))
                    if x != 0:
                        Player1.ChooseYourSkills(x)
                        if Player1.MpIsEnough == 0:
                            print('请重新选择其他技能进行释放!')
                            continue
                        else:
                            if Player1.skill[x] == '治疗术':
                                TempMonster.ChooseMonsterSkills(Player1)
                                TempMonster.MonsterSkillsTo(Player1)
                                print('玩家%s使用了 %s 回复了%d点生命值!' % (Player1.name, Player1.skill[x], 100))
                                print('怪物%s使用了 %s 对玩家%s造成了%d点伤害!' % (
                                TempMonster.name, TempMonster.TempSkill, Player1.name, TempMonster.SkillDamage))
                            else:
                                Player1.SkillsTo(TempMonster)
                                TempMonster.ChooseMonsterSkills(Player1)
                                TempMonster.MonsterSkillsTo(Player1)
                                print('玩家%s使用了 %s 对怪物%s造成了%d点伤害!' % (
                                Player1.name, Player1.skill[x], TempMonster.name, Player1.SkillDamage))
                                print('怪物%s使用了 %s 对玩家%s造成了%d点伤害!' % (
                                TempMonster.name, TempMonster.TempSkill, Player1.name, TempMonster.SkillDamage))
                    if random.randint(1, 100) > 25:
                        if datalist:
                            TempSkill = random.choice(datalist)
                            print('怪物%s掉落了技能 %s !已自动拾取!' % (TempMonster.name, TempSkill))
                            Player1.skill.append(TempSkill)
                            datalist.remove(TempSkill)
                    if Player1.hp <= 0:
                        Player1.hp = 0
                        print('玩家%s已经死亡!游戏结束!' % Player1.name)
                        break
                    if TempMonster.hp <= 0:
                        TempMonster.hp = 0
                        Player1.exp += 50
                        if Player1.exp == 100:
                            Player1.level += 1
                            Player1.hp += 500
                            Player1.mp += 500
                            Player1.attack += 10
                            Player1.defend += 10
                            Player1.exp = 0
                            Player1.MpIsEnough = 1
                        print('恭喜你已成功击败怪物%s!' % TempMonster.name)
                        print('----------回合结束!----------')
                        round = 1
                        NotInRound = 1
                        break
                    round += 1
        else:
            print('结束游戏!')
            break


if __name__ == '__main__':
    main()

小结:其中的很多数据和方法并不完善,并且代码肯定也还有精简的地方。实现的思路主要是创建一个角色Role的大类,包含血量、蓝量、攻击力和防御力等基本属性,然后再以Role作为父类,继承出子类玩家Player和怪物Monster,再分别给这两类增加不同的属性和方法。最后在main()函数中以一个while循环作为游戏进程即可。

3.今日总结

自从接触了面向对象编程之后,我才真切感受到了python语言的可读性之强,即使是自己写的自定义类,在主函数中也依然能读懂各种方法的作用,对于代码的维护和修改有着很大的帮助。不足之处是还应该多尝试着应用各种方法,而不是只用实例方法,并且也要活用__slots__和@property。
同时,如果想要更进一步把面向对象练熟练透的话,我建议认真阅读一下python中所有的魔法方法使用指南:
(译)Python魔法方法指南

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值