⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️
⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️
⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️
上篇回顾:
上篇我们帮天下第一武道会的选手登记了信息,编写了 hit()、defend() 函数,并结合 while 循环实现了决斗过程。然而也遇到了两大问题:一是如何设计出一套模板,优化登记选手信息过程;二是如何设计一套 能体现出动作发起方与承受方 的函数。
想解决这些问题,需要我们转变思维方式,用 面向对象 方式分析问题。所谓 面向对象编程,就是将编程视作 对象 的集合,从 对象与对象间的交互 角度出发思考问题。
与此同时,我们还分析出了 选手类 具有的特征与行为:
从整体上分析出决斗过程中对象与对象间有什么样的交互后,我们就可以正式开始编写代码啦。下面让我们一起来定义选手类吧!
类与对象
在 Python 中,我们用关键字 class 来定义类。比如我们编写 class Player:,就定义好了一个名为 Player 的类:
class Player:
  pass代码第 1 行的类名 Player 后有一个冒号 :,表示后面的语句都是类的具体内容。所有归属于类管辖的语句,都要增加一层缩进。这和我们之前学习的分支、循环、函数结构是一样的道理。
可以看到,现在 Player 类中只有一条语句 pass。这是 Python 中的特殊语句,没有实际含义,Python 在执行到它时也什么都不会做。不过它能够保证结构的完整性。例如,我们只写了一行 while True:,Python 会警告我们循环体为空;而如果我们在循环体位置添加一行 pass,Python 就不会报错了:
# 循环体为空,Python 会报错
while True:
# 虽然循环体内的 pass 没有实际含义
# 但它补全了 while 循环结构,因此 Python 不会报错
while True:
  pass类中可以包含若干 属性 和 方法。属性 用于描述对象的特征,方法 用来定义对象的行为。比如说,虽然信息表中没有包含这些信息,但我们知道,选手们都有两只胳膊、两条腿、一双眼睛。胳膊、腿、眼睛的数量,就是 Player 类的特征,我们在代码中可以这样表达:
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛啊哈,原来类的 属性 就是定义在类中的 变量。
定义好类后运行代码,却发现结果区域里什么也没有。这是怎么一回事呢?
这是因为,我们所定义的 类 只是一套模板。就像在生产零件时不能只有图纸,还要交给机器加工出一个个看得见摸得着的实物;我们有了模板后,也要创建出 实例(instance)才能实现对象与对象间的交互。
而创建类实例的过程,就叫做 类的实例化
类的实例化
说起来很玄乎,实际上,它对应的代码十分简单:
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛
# 实例化 Player 类,生成一个新的实例对象
kakarot = Player()代码第 8 行的含义是实例化 Player 类,并将生成的新的实例对象赋值给变量 kakarot。此时的变量 kakarot 就是 Player 类实例化出来的实例 对象(object)。
是的,我们创建的变量也是 对象!
拓展:实例 和 对象 是两个不同的词。当我们强调 类与对象之间的关系 时,会说“
kakarot是Player的实例”;当我们 单独提起某一具体实例时,通常会说“对于kakarot对象……”。
Python 奉行 万物皆对象 理念,你看到的“变量”,其实是一个个由基础的 数据类型类 实例化而来的实例对象。我们使用 type() 函数查看变量的数据类型,实际上就是在查看它所归属的 类。
同样的道理,我们也可以用 type() 函数查看变量 kakarot 的类型。
比如:
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player()
# 打印 kakarot 变量的数据类型
print(type(kakarot))# 输出结果为:
# <class '__main__.Player'>
这里的 __main__ 就是我们之前学习过的 程序入口,即我们刚刚运行的 main.py 文件。Player 是我们在文件中定义的类,__main__ 和 Player 中间的 . 运算符,表明了它们之间的从属关系,即变量 kakarot 属于我们在当前文件所定义的 Player 类型。
我们不是第一次见到 . 运算符啦。开动脑筋想一想,我们之前还在哪里遇到了它呢?
😉 没错!我们想调用列表、字符串或字典的特殊方法时,变量名与方法名之间要用 . 连接;使用某个模块中的变量或函数时,模块名和变量名/函数名之间,也要用 . 连接。
# 调用方法时,变量名和方法名之间用 . 连接
months = ['一月', '二月']
months.append('三月')
# 使用模块中某个变量或函数时,也要用 . 连接
import random
number = random.randint(1, 100)放到类中也是一样的道理。当我们想要访问 对象的属性 时,需要用 . 运算符连接对象名和属性名,就像这样:
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player()
print('卡卡罗特有 {} 只胳膊'.format(kakarot.arm_num))
# 输出:卡卡罗特有 2 只胳膊注意看代码第 8 行,我们通过 kakarot.arm_num 访问到了 kakarot 的 arm_num 属性。这种用 . 运算符访问数据的方式,更贴合我们的认知方式。因此用面向对象思想编写的代码,也更容易阅读。
属性定义与修改
看着我们创建好的 Player 类,天津饭(Tien Shinhan)选手提出异议:“我是三目人与地球人后裔,天生三目,怎么能用‘一双眼睛’概括我的特征呢?不行,你得给我把我的第三只眼加上!”
面对这样的 特例,我们可以为天津饭选手单独修改 eye_num 属性:
# 类的定义与实例化代码省略
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 tien
tien = Player()
# 将他的眼睛数量修改为三只
tien.eye_num = 3
print('天津饭选手有 {} 只眼睛'.format(tien.eye_num))
# 输出:天津饭选手有 3 只眼睛有的同学可能会好奇:既然对象是由类实例化而来,那我们改变对象的属性后,其它由同一类实例化而来的对象属性,是否也跟着发生了变化呢?
编程练习
请按照注释提示,将天津饭的眼睛数量修改为 3 只;接着创建一个新的 Player 类对象,看看他有几只眼睛。
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 tien
tien = Player()
# 将他的眼睛数量修改为三只
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
# 输出他的眼睛数量优化后的代码为:
# 选手类
class Player:
  arm_num = 2 # 两只胳膊
  leg_num = 2 # 两条腿
  eye_num = 2 # 一双眼睛
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 tien
tien = Player()
# 将他的眼睛数量修改为三只
tien.eye_num = 3
print('天津饭的眼睛数量为{}只'.format(tien.eye_num))
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player()
# 输出他的眼睛数量
print('卡卡罗特的眼睛数量为{}只'.format(kakarot.eye_num))# 输出结果为:
# 天津饭的眼睛数量为3只
# 卡卡罗特的眼睛数量为2只
看来我们躲过了跟天津饭一样生出第三只眼睛的命运。不过这背后是怎么一回事呢?大家都是由 Player 类实例化而来的对象,为什么彼此之间不会干扰呢?
前面我们提到过,类是抽象的模板,对象可以看作生产出的一个个零件。零件与零件是 相互独立 的,我们加工 tien“零件”,自然不会影响到 kakarot“零件”,更不会对模板本身,即 Player 类本身产生影响。
了解了如何为类添加属性,我们再来看看如何为类添加 方法。
方法定义与调用
类的 方法 用来描述对象的 行为。例如,每位选手都需要登记信息,那么我们可以定义一个 sign_up() 方法,帮指定选手登记姓名、生命值和攻击力信息。
拓展:sign up 意为注册、登记;与之对应的,sign in 表示登录、签到。
# 选手类
class Player:
  # 帮选手登记信息
  def sign_up(self, name, HP, ATK): # 这里的 self 指的是需要登记信息的选手
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    print('+ {}\tHP: {}\tATK: {}'.format(name, HP, ATK))啊哈,原来所谓 方法,就是定义在类中的 函数。调用方法的方式和访问、修改对象属性类似,依然通过 . 运算符完成:
kakarot = Player()
Player.sign_up(kakarot, '卡卡罗特', 100 ,25)
# 输出:
# 已成功登记信息
# + 卡卡罗特    HP: 100 ATK: 25上面代码第 1 行,我们实例化了 Player 类,并将新生成的实例对象赋值给了 kakarot。接着在第 2 行,Player 类调用自己的 sign_up() 方法,传入了 kakarot 等参数,为卡卡罗特登记信息。
这是比较符合 面向过程编程 思维方式的编写形式。实际上,有了 类与对象 的加持,我们可以写成这样:
kakarot = Player()
kakarot.sign_up('卡卡罗特', 100 ,25)
# 输出:
# 已成功登记信息
# + 卡卡罗特    HP: 100 ATK: 25注意看第 2 行代码,此时是 kakarot 实例对象在调用 Player() 类的 sign_up() 方法。它与之前我们编写的 Player.sign_up(kakarot, '卡卡罗特', 100 ,25) 是等价的:
self 参数含义
 
还真说对了。当 实例对象调用方法 时,Python 会 强制性 地将 实例对象本身 传递给类方法的 第一个参数。
因此,虽然我们在调用方法时写的是 kakarot.sign_up('卡卡罗特', 100 ,25),实际上对 Python 而言,它运行的是 Player.sign_up(kakarot, '卡卡罗特', 100 ,25)。
这样一来,一是简化了代码,二是更能看清楚是什么对象产生了什么样的行为。就像老师在上一关末尾为你展示的代码结构一样:
kakarot = Player('卡卡罗特', 100, 25)
piccolo = Player('比克大魔王', 150, 15)
kakarot.hit(piccolo)即使我们不清楚 hit() 方法内部细节,但我们一眼就能看出 kakarot.hit(piccolo) 的含义是 卡卡罗特打了比克大魔王一拳。这其中体现了 类 的 封装 特征。
除了 封装,类还有 继承 与 多态 两大特征,这些内容我们会在下一章深入了解。
当对象调用类方法时,Python 会自动地把对象自身传递给 self 参数,因此我们只需要传入 self 后面的参数即可。A 选项多传递了 kuririn 对象本身,错误;D 选项是 kakarot 在调用方法,因此会为 kakarot 登记信息,错误。
而如果是类调用方法,则需要在参数列表中写明,究竟是什么对象在发生这一行为,因此要写成 Player.sign_up(kuririn, '克林', 85, 12),B 选项错误。
敲黑板划重点啦。Python 是 强制性 地将 实例对象本身 传递给类方法的 第一个参数,而不是强制性传递给名为 self 的参数。假如我们在定义 sign_up() 方法时,人为地把 self 参数放到参数列表后面位置:
# 选手类
class Player:
  # 人为地把 self 参数放到参数列表后面位置
  def sign_up(name, HP, ATK, self):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
kakarot = Player()
kakarot.sign_up('卡卡罗特', 100 ,25)当 Python 执行到最后一行时,它依然会把 kakarot 传递给参数列表中第一个参数,也就是 name 参数,接着把 '卡卡罗特' 传递给 HP 参数……一切都乱套啦!
所以我们在定义类方法时,一定要把 self 参数放在参数列表第一项 哦。
明白如何编写方法后,我们来尝试将上一关编写的 hit() 函数改写成 Player 类的 hit() 方法吧。
通过之前分析我们知道,攻击 行为是由一个 Player 类对象发出的,会打在另一个 Player 对象身上。因此 hit() 方法需要携带两个参数,第一个参数自然是表示自身的 self;第二个是他攻击的对象 target:
# 选手类
class Player:
  # 帮选手登记信息
  def sign_up(self, name, HP, ATK):
    pass
  # 发动攻击
  def hit(self, target):
    pass这一行为具体是怎么一回事呢?这中间有两个环节,一是攻击者放狠话,二是被攻击的对象调用自己的 防御 能力,抵御受到的伤害。我们将 hit() 方法补充完成,再把 防御 行为抽象成 defend() 方法,此时的类结构是这样的: 
# 选手类
class Player:
  # 帮选手登记信息
  def sign_up(self, name, HP, ATK):
    pass
  # 发动攻击
  def hit(self, target):
    print('>> 【{}】向【{}】发动攻击,\n'.format(self.name, target.name))
    target.defend(self.ATK)
  
  # 防御攻击
  def defend(self, damage):
    pass接下来,我们只要再补充完成 defend() 方法,就能完成整个 Player 类的设计了。这一步就交给你来完成吧。
编程练习
请按照注释提示,将 defend() 函数改写为 Player 类的方法,并在程序最后一行让卡卡罗特向比克大魔王发动一次攻击。
代码提示
- 类方法归属类管辖,需要添加一层缩进;
- defend()方法除- self外还应接受一个参数,表示受到了多少伤害。
注意事项
访问、修改实例对象的属性时,需要用 . 运算符。
操作提示:在代码编辑区域选中若干行,按下键盘上的
tab键,即可快速为这些行添加一层缩进;若选中若干行后按下shift + tab键,则能为这些行减少一层缩进。不妨自己试试吧~
在此代码中进行优化:
from random import randint
# 选手类
class Player:
  # 帮选手登记信息
  def sign_up(self, name, HP, ATK):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    print('+ {}\tHP: {}\tATK: {}\n'.format(name, HP, ATK))
  # 发动攻击
  def hit(self, target):
    print('>> 【{}】向【{}】发动攻击,\n'.format(self.name, target.name))
    target.defend(self.ATK)
# 请将下面 defend() 函数改写为 Player 类的方法
def defend(defender, damage):
  # 若生成的随机数小于等于 20,则闪避成功
  if randint(0, 100) <= 20:
    print('>> 【{}】完美躲避了攻击!\n'.format(defender['name']))
  # 否则扣除对应生命值
  else:
    defender['HP'] = defender['HP'] - damage
    print('>> 【{}】受到 {} 点伤害...\n'.format(defender['name'], damage))
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player()
# 为卡卡罗特登记信息
kakarot.sign_up('卡卡罗特', 100 ,25)
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 piccolo
piccolo = Player()
# 为比克大魔王登记信息
piccolo.sign_up('比克大魔王', 150, 15)
# 让卡卡罗特向比克大魔王发起攻击优化后的代码为:
from random import randint
# 选手类
class Player:
  # 帮选手登记信息
  def sign_up(self, name, HP, ATK):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    print('+ {}\tHP: {}\tATK: {}\n'.format(name, HP, ATK))
  # 发动攻击
  def hit(self, target):
    print('>> 【{}】向【{}】发动攻击,\n'.format(self.name, target.name))
    target.defend(self.ATK)
# 请将下面 defend() 函数改写为 Player 类的方法
  def defend(defender, damage):
    # 若生成的随机数小于等于 20,则闪避成功
    if randint(0, 100) <= 20:
      print('>> 【{}】完美躲避了攻击!\n'.format(defender.name))
    # 否则扣除对应生命值
    else:
      print(defender.name)
      defender.HP = defender.HP - damage
      print('>> 【{}】受到 {} 点伤害...\n'.format(defender.name, damage))
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player()
# 为卡卡罗特登记信息
kakarot.sign_up('卡卡罗特', 100 ,25)
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 piccolo
piccolo = Player()
# 为比克大魔王登记信息
piccolo.sign_up('比克大魔王', 150, 15)
# 让卡卡罗特向比克大魔王发起攻击
Player.hit(kakarot, piccolo)# 输出结果为:
# 已成功登记信息
# + 卡卡罗特 HP: 100 ATK: 25
# 已成功登记信息
# + 比克大魔王 HP: 150 ATK: 15
# >> 【卡卡罗特】向【比克大魔王】发动攻击,
# >> 【比克大魔王】完美躲避了攻击!
不知道你发现没有,虽然 sign_up() 和 hit()、defend() 都是 Player 类的方法,但它们的使用频率和时机是不一样的:sign_up() 方法只有创建完实例对象后使用了一次,并且,如果不事先为选手登记信息,后续的攻击、防御过程都是没有意义的。🤔️ 那我们可不可以把这种 一实例化就必须要做的行为 与类实例化过程绑定在一起呢?
当然可以啦~我们用 __init__() 方法实现这一点。
__init__() 方法
 
__init__() 方法和程序入口 __main__ 一样,都以两个下划线 __ 开头、两个下划线结束。这意味着 __init__ 也是 Python 中内置的一个名称,有着特殊的功能。
init 的全称是 initialize(初始化),会在我们实例化新的对象时 自动调用。
比如说我们希望在创建 Player 类对象时,自动地为这名新选手登记信息,那么可以把原来 sign_up() 方法中的内容挪到 __init__() 方法中:
# 选手类
class Player:
  # 每当创建新对象,都自动为他登记信息
  def __init__(self, name, HP, ATK):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    print('+ {}\tHP: {}\tATK: {}\n'.format(name, HP, ATK))此时我们再创建 Player 类的对象,需要在 Player() 的圆括号中传入姓名、生命值、攻击力三样信息,就像这样:
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player('卡卡罗特', 100, 25)
# 输出:
# 已成功登记信息
# + 卡卡罗特    HP: 100 ATK: 25Python 执行到 kakarot = Player('卡卡罗特', 100, 25) 时,实际上完成了两个步骤:
- 实例化
Player类,生成一个新的实例,将其赋值给kakarot;
kakarot自动地调用__init__()方法,为自己的name等属性赋值。

注意,一旦我们在类中编写了 __init__() 方法,那么在创建实例时 必须要传入对应的参数。假如我们没有传入对应参数,那么 Python 会报错喔:
# 未传入参数
kakarot = Player()
# 输出:
# TypeError: __init__() missing 3 required arguments: 'name', 'HP', 'ATK'
# 类型错误:__init__() 需要 3 个参数:'name','HP' 和 'ATK'单选题我们将 sign_up() 方法改为 __init__() 方法后,以下哪项可以为 克林 选手(kuririn 对象)登记信息呢?
克林信息:生命值 85,攻击力 12。
A:
kuririn = Player.__init__(kuririn, '克林', 85, 12)
B:kuririn = Player('克林', 85, 12)
对于 A 选项来说,Python 会先执行 Player.__init__(kuririn, '克林', 85, 12),而此时我们还没有定义 kuririn 变量,自然无法将它作为参数传递给 __init__() 方法,所以 A 选项错误。
对于 B 选项来说,Python 会先执行 Player('克林', 85, 12)。这其中发生了两个步骤,第一个步骤是实例化 Player 类,生成实例对象,将其赋值给 kuririn;第二步是 kuririn 自动地调用 __init__() 方法,为 name 等属性赋值。所以 B 选项是正确的。
如果还不清楚的话,可以再反复读几遍,或者自己编写代码做做实验哦。
咳咳,编写了 __init__() 方法后我们再实例化 Player 类时,看起来很像在调用一个名为 Player 的函数。为避免引发歧义,凸显出类是一个整体性的概念,Python 官方建议为类取名时 首字母大写,以便和函数名区分开来。如果所要描述的类不能用单个单词表达,则每个单词的首字母都需要大写,比如 FilmSelector。同学们在自己编写类时也要注意这一点哦。
好了,本章学习就到这里啦,让我们用一张图总结一下类与对象的语法知识吧:

课后练习:卡卡罗特VS比克大魔王
请你根据注释中提示信息,结合本关所学知识,用 面向对象编程 方法编写完成 Player 类,并让卡卡罗特和比克大魔王展开决斗吧~
卡卡罗特(kakarot)
- 姓名(name):卡卡罗特
- 生命值(HP):100
- 攻击力(ATK,attack):25
比克大魔王(piccolo)
- 姓名(name):比克大魔王
- 生命值(HP):150
- 攻击力(ATK,attack):20
决斗说明
- 决斗采取 回合制,由卡卡罗特先发动攻击;
- 防御时有 20% 几率防御成功,完全闪避攻击,免受伤害。
请在下方代码中,实现对应功能:
from random import randint
# 选手类
class Player:
  # 每当创建新对象,都自动为他登记信息
  def __init__(self, name, HP, ATK):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    self.show_info()
  
  # 展示信息
  def show_info(self):
    print('+ {}\tHP: {}\tATK: {}\n'.format(self.name, self.HP, self.ATK))
  
  # 发动攻击
  # 防御攻击
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 piccolo
# 展开决斗
while True:
  # 每回合开始,由卡卡罗特先发动攻击
  
  # 判断此时决斗是否分出胜负
  
  # 若未分出胜负,则攻防交换,由比克大魔王发动攻击
  
  # 判断此时决斗是否分出胜负
  
  # 每回合结束,打印出两人当前信息实现后的代码为:
from random import randint
# 选手类
class Player:
  # 每当创建新对象,都自动为他登记信息
  def __init__(self, name, HP, ATK):
    self.name = name  # 为选手登记姓名
    self.HP = HP      # 为选手登记生命值
    self.ATK = ATK    # 为选手登记攻击力
    print('已成功登记信息')
    self.show_info()
  
  # 展示信息
  def show_info(self):
    print('+ {}\tHP: {}\tATK: {}\n'.format(self.name, self.HP, self.ATK))
  # 发动攻击
  def hit(attack, defender):
    print('-' * 6,attack.name,'发起攻击','-' * 6)
    defender.dfAttack(attack.ATK)
  # 防御攻击
  def dfAttack(defender, atk):
    if randint(1,100) <= 20:
      # 防御成功
      print(defender.name,'防御成功')
    else:
      # 防御失败
      defender.HP -= atk
      print(defender.name,'防御失败,气血-',atk)
    Player.show_info(defender)
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 kakarot
kakarot = Player('卡卡罗特', 100, 25)
# 实例化 Player 类,生成一个新的实例对象,赋值给变量 piccolo
piccolo = Player('比克大魔王', 150, 20)
# 展开决斗
while True:
  # 每回合开始,由卡卡罗特先发动攻击
  Player.hit(kakarot, piccolo)
  # 判断此时决斗是否分出胜负
  if piccolo.HP <= 0:
    print(kakarot.name,'胜出')
    break
  # 若未分出胜负,则攻防交换,由比克大魔王发动攻击
  else:
    Player.hit(piccolo, kakarot)
    if kakarot.HP <= 0:
      # 判断此时决斗是否分出胜负
      print(piccolo.name,'胜出')
      break
  
  # 每回合结束,打印出两人当前信息
  # Player.show_info(kakarot)
  # Player.show_info(piccolo)
  print('-' * 32 + '\n')
课后练习:空调说明书
空调说明书
夏天怎么能没有空调呢!小贝舒舒服服地吹着凉风,她可太喜欢家里的空调了。
下面是小贝家卧室空调的说明书:
品牌:四季
额定电压:220V
额定制冷功率:1000W
额定制热功率:1350W
AC 是空调 (air conditioner) 的英文缩写,请你补全代码中标有 ??? 的地方,为 AC 类添加一个初始化方法,让 intro 方法执行时,能够打印出上面的空调说明书。
# brand 品牌,voltage 电压,cold 制冷,hot 制热
class AC:
  def __init__(???):
    ???
  def intro(self):
    print('''品牌:{}
额定电压:{}
额定制冷功率:{}
额定制热功率:{}'''.format(???))
air_bedroom = AC(???)
air_bedroom.intro()优化后可得:
# brand 品牌,voltage 电压,cold 制冷,hot 制热
class AC:
  def __init__(self,brand, voltage, cold, hot):
    self.brand = brand
    self.voltage = voltage
    self.cold = cold
    self.hot = hot
  def intro(self):
    print('''品牌:{}
额定电压:{}
额定制冷功率:{}
额定制热功率:{}'''.format(self.brand, self.voltage, self.cold, self.hot))
air_bedroom = AC('四季','220V','1000W','1350W')
air_bedroom.intro()
# 输出结果为:
# 品牌:四季
# 额定电压:220V
# 额定制冷功率:1000W
# 额定制热功率:1350W
















