python类与面向对象编程

⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️

⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️

⚠️⚠️⚠️本章后半部分难度激增,请一定认真学习⚠️⚠️⚠️

上篇回顾:

上篇我们帮天下第一武道会的选手登记了信息,编写了 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 类的方法,并在程序最后一行让卡卡罗特向比克大魔王发动一次攻击。

代码提示

  1. 类方法归属类管辖,需要添加一层缩进;
  2. 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: 25

Python 执行到 kakarot = Player('卡卡罗特', 100, 25) 时,实际上完成了两个步骤:

  1. 实例化 Player 类,生成一个新的实例,将其赋值给 kakarot
  2. 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

决斗说明

  1. 决斗采取 回合制,由卡卡罗特先发动攻击;
  2. 防御时有 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

相关推荐

  1. Python面向对象-对象

    2024-06-07 11:46:03       56 阅读
  2. Python面向对象编程:入门篇(对象

    2024-06-07 11:46:03       45 阅读
  3. 三、C#面向对象编程对象

    2024-06-07 11:46:03       57 阅读
  4. 第八章 对象面向对象编程(上)

    2024-06-07 11:46:03       52 阅读
  5. python--面向对象-3

    2024-06-07 11:46:03       34 阅读
  6. Python面向对象编程

    2024-06-07 11:46:03       62 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-07 11:46:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-07 11:46:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-07 11:46:03       82 阅读
  4. Python语言-面向对象

    2024-06-07 11:46:03       91 阅读

热门阅读

  1. 探索Web前端三大主流框架:React,Angular和Vue.js

    2024-06-07 11:46:03       27 阅读
  2. MongoDB 分布式 概述

    2024-06-07 11:46:03       27 阅读
  3. CSS中的长度单位详解

    2024-06-07 11:46:03       34 阅读
  4. 【面试题】Node.js高频面试题

    2024-06-07 11:46:03       24 阅读
  5. 开发常用的组件库

    2024-06-07 11:46:03       35 阅读
  6. Oracle 误删数据后回滚

    2024-06-07 11:46:03       26 阅读
  7. 正则表达式基础

    2024-06-07 11:46:03       32 阅读