目录

  • Day.10
    • 1.面向对象进阶
      • i.装饰器@property
      • ii.__slots__魔法
      • iii.静态方法和类方法
      • iv.类之间的关系
    • 2.综合案例练习
    • 3.今日总结

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 = nameself._age = age# 访问器 - getter方法@propertydef name(self):return self._name# 访问器 - getter方法@propertydef age(self):return self._age# 修改器 - setter方法@age.setterdef 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 randomclass Role:name = ''hp = 500mp = 500attack = 100defend = 50damage = 0# 伤害计算公式: 伤害=自己攻击力-对手防御力def __init__(self, name, hp, mp, attack, defend):self.name = nameself.hp = hpself.mp = mpself.attack = attackself.defend = defend# 普通攻击:def AtkTo(self, other):self.damage = self.attack - other.defendother.hp -= self.damageclass Player(Role):exp = 0level = 1skill = ['普通攻击']SkillDamage = 0MpIsEnough = 1def __init__(self, name, hp, mp, attack, defend, exp, level, SkillDamage):Role.__init__(self, name, hp, mp, attack, defend)self.exp = expself.level = levelself.SkillDamage = SkillDamage# 技能攻击:def SkillsTo(self, other):self.SkillDamage = self.SkillDamage - other.defendother.hp -= self.SkillDamage# 选择技能:def ChooseYourSkills(self, x):if self.skill[x] == '肉蛋葱鸡':if self.mp >= 90:self.MpIsEnough = 1self.mp -= 90self.SkillDamage = 270else:self.MpIsEnough = 0print('对不起!您的蓝量不足,无法再释放技能!')elif self.skill[x] == '我夹':if self.mp >= 50:self.MpIsEnough = 1self.mp -= 50self.SkillDamage = 90else:self.MpIsEnough = 0print('对不起!您的蓝量不足,无法再释放技能!')elif self.skill[x] == '起飞':if self.mp >= 70:self.MpIsEnough = 1self.mp -= 70self.SkillDamage = 200else:self.MpIsEnough = 0print('对不起!您的蓝量不足,无法再释放技能!')elif self.skill[x] == '治疗术':if self.mp >= 10:self.MpIsEnough = 1self.mp -= 10self.SkillDamage = 0self.hp += 100else:self.MpIsEnough = 0print('对不起!您的蓝量不足,无法再释放技能!')else:if self.mp >= 100:self.MpIsEnough = 1self.mp -= 100self.SkillDamage = 400else:self.MpIsEnough = 0print('对不起!您的蓝量不足,无法再释放技能!')class Monster(Role):SkillDamage = 0skill = []MpIsEnough = 1TempSkill = ''def __init__(self, name, skill, hp, mp, attack, defend, SkillDamage):Role.__init__(self, name, hp, mp, attack, defend)self.SkillDamage = SkillDamageself.skill = skilldef MonsterSkillsTo(self, other):self.SkillDamage = self.SkillDamage - other.defendif self.SkillDamage <= 0:self.SkillDamage = 0other.hp -= self.SkillDamageelse:other.hp -= self.SkillDamagedef ChooseMonsterSkills(self, other):self.TempSkill = random.choice(self.skill)if self.TempSkill == '普通攻击':self.SkillDamage = self.attack - other.defendelif self.TempSkill == '车轮滚滚':self.SkillDamage = 270elif self.TempSkill == '我夹':self.SkillDamage = 90elif self.TempSkill == '大地震击':self.SkillDamage = 200elif self.TempSkill == '势不可挡':self.SkillDamage = 400elif self.TempSkill == '疏通肠道':self.SkillDamage = 280elif self.TempSkill == '泰坦之怒':self.SkillDamage = 70elif self.TempSkill == '暗流涌动':self.SkillDamage = 175elif self.TempSkill == '深海葱鸡':self.SkillDamage = 80elif self.TempSkill == '斩钢闪':self.SkillDamage = 120elif self.TempSkill == '风之障壁':self.SkillDamage = 0elif self.TempSkill == '踏前斩':self.SkillDamage = 100else:self.SkillDamage = 400def main():NotInRound = 1Player1 = 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 = 1datalist = ['肉蛋葱鸡', '我夹', '起飞', '千层饼', '治疗术']y = int(input('按1进入游戏,按其他键退出游戏:'))while True:if y == 1 and Player1.hp > 0:if NotInRound == 1:NotInRound = 0TempMonster = random.choice(MonsterList)if TempMonster.name == '墨菲特':TempMonster.hp = 1000TempMonster.mp = 0TempMonster.attack = 50TempMonster.defend = 10if TempMonster.name == '深海泰坦':TempMonster.hp = 2000TempMonster.mp = 0TempMonster.attack = 10TempMonster.defend = 50if TempMonster.name == '亚索':TempMonster.hp = 200TempMonster.mp = 0TempMonster.attack = 200TempMonster.defend = 10print('遭遇怪物%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('请重新选择其他技能进行释放!')continueelse: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 = 0print('玩家%s已经死亡!游戏结束!' % Player1.name)breakif TempMonster.hp <= 0:TempMonster.hp = 0Player1.exp += 50if Player1.exp == 100:Player1.level += 1Player1.hp += 500Player1.mp += 500Player1.attack += 10Player1.defend += 10Player1.exp = 0Player1.MpIsEnough = 1print('恭喜你已成功击败怪物%s!' % TempMonster.name)print('----------回合结束!----------')round = 1NotInRound = 1breakround += 1else:print('结束游戏!')breakif __name__ == '__main__':main()

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

3.今日总结

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

Python学习日记10相关推荐

  1. Python学习日记-pandas操作学习

    Python学习日记-pandas模块学习 根据需求筛选数据 绘制柱状图-利用pandas绘制 绘制柱状图-利用matplotlib绘制 绘制柱状图-两组数据比较 绘制柱状图-叠加柱状图 绘制饼状图 ...

  2. python学习日记2-3周

    python学习日记2-3周 怠惰怠惰 improt 导入 import phone.apple.iphone6 import phone.apple.iphone7 import phone.sam ...

  3. Python学习日记——罗马数字转整数

    Python学习日记004--罗马数字转整数 题目来源:LeetCode题库--罗马数字转整数 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 ...

  4. Python学习日记-day1基础篇 字符 输出 注释

    Python学习日记-day1基础篇 字符 输出 注释 by北栀一刺 # -*- coding: utf-8 -*- """ Spyder EditorThis is a ...

  5. Python学习笔记--10.Django框架快速入门之后台管理admin(书籍管理系统)

    Python学习笔记--10.Django框架快速入门之后台管理 一.Django框架介绍 二.创建第一个Django项目 三.应用的创建和使用 四.项目的数据库模型 ORM对象关系映射 sqlite ...

  6. python学习日记(第一周)

    python学习日记(第一周) 前言: 无论出于什么目的,学个程序语言吧.之前学过一点点基础语法,然后就搁置了两年,日记仅作为个人记录,有问题还望不吝赐教 其实这玩意儿应该每次学一部分记录一部分的,好 ...

  7. 菜菜的Python学习日记 | Python类实验代码分享

    系列索引:菜菜的Python学习日记 | Python从入门到入土详解 Python类实验代码分享 编写程序,实现以下功能. (1)创建员工类Employee,属性有姓名name.能力值ability ...

  8. 菜菜的Python学习日记 | 正则表达式你必须了解的知识点

    系列索引:菜菜的Python学习日记 | Python从入门到入土详解 文章目录 常用规则 Python对正则表达式的支持 常用规则 符号 解释 示例 说明 . 匹配任意字符 b.t 可以匹配bat ...

  9. Python学习日志10 - 高阶函数和高级应用

    Python学习日志 RBHGO的主页欢迎关注 温馨提示:创作不易,如有转载,注明出处,感谢配合~ 目录 文章目录 Python学习日志 目录 前言 进入正题 Python学习日志10课 - 高阶函数 ...

最新文章

  1. HTTP详解(1)-工作原理【转】
  2. java线程模拟生产者消费者问题
  3. 修改 Android 5.1 默认设置
  4. myeclipse安装、导入一个项目、解决2个程序错误、解决运行错误、运行项目
  5. qt 进度条_Qt开源作品12-硬盘容量控件
  6. jPlayer插件的使用
  7. 后悔!我早该把这1W字详解的 InnoDB 原理给你!
  8. 下拉插件dropload js时间计算(几天前)
  9. ImportError: cannot import name ‘AliPay‘ from ‘alipay‘
  10. 数字图像处理实验——Python语言实现
  11. java Flink滚动时间窗口聚合TumblingProcessingTimeWindows运算例子
  12. 安卓调用系统录像功能:1、启动录像返回视频,2、启动录像将视频存储在指定路径下
  13. java反射机制(4)动态代理初探
  14. bootstrap EF_Bootstrap优秀模板INSPINIA.2.9.2
  15. C#将大量数据批量写入Excel中
  16. 阿里云(企业云解析DNS)让你的博客飞起来
  17. 关于amd cpu超频 个人心得
  18. Chia命令行P图工具
  19. iOS 新浪新闻首页卡片滚动特效实现
  20. Appro DM36x IPNC 4.0 开发环境配置

热门文章

  1. mysql 经纬度距离计算
  2. 解决win10系统WiFi间歇性掉线,只有重启电脑才能连接WiFi的问题
  3. 换脸插件 php,换脸系列——整脸替换
  4. Android实验二:Activity的生命周期
  5. Java案例----双色球中奖分析
  6. 如何清空历史的告警记录——WGCLOUD
  7. 导出Excel设置文件名
  8. 经典语录,经常看看感悟人生!
  9. 今天是某年、某月、某日
  10. 185Echarts - 日历坐标系(Calendar Lunar)