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 = 3print('天津饭选手有 {} 只眼睛'.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'] - damageprint('>> 【{}】受到 {} 点伤害...\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 - damageprint('>> 【{}】受到 {} 点伤害...\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 -= atkprint(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 = brandself.voltage = voltageself.cold = coldself.hot = hotdef intro(self):print('''品牌:{}
额定电压:{}
额定制冷功率:{}
额定制热功率:{}'''.format(self.brand, self.voltage, self.cold, self.hot))air_bedroom = AC('四季','220V','1000W','1350W')
air_bedroom.intro()# 输出结果为:
# 品牌:四季
# 额定电压:220V
# 额定制冷功率:1000W
# 额定制热功率:1350W

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/23941.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

冯喜运:6.7今日外汇黄金原油走势分析及日内操作策略

【黄金消息面分析】&#xff1a;美国初请失业金人数超预期&#xff0c;市场对美联储9月降息预期升温&#xff0c;全球降息潮起&#xff0c;黄金市场受支撑。北京时间本周四&#xff0c;美国劳工部公布的数据显示&#xff0c;截至6月1日当周初请失业金人数增加至22.9万人&#x…

docker bash: vi: command not found 修改文件无法使用 vi yum的方法

如题&#xff0c;被入坑很多次。也参考了很多的修复docker 中的vi yum等方法。最终都未解决。 因为要修改 已安装容器中的各类配置信息。无法使用vi yum很麻烦。除去使用docker 挂载文件方法外&#xff0c;还可以使用如下方法直接修改对应的配置文件信息。 如: 修改 logstas…

短剧系统投流版开发,为运营公司投流业务赋能

短剧系统投流版开发是一项复杂的任务&#xff0c;旨在为运营公司的投流业务提供强大的技术支持和赋能。以下是一些关键步骤和考虑因素&#xff0c;以确保短剧系统投流版的成功开发&#xff1a; 一、明确业务需求与目标 首先&#xff0c;需要深入了解运营公司的业务需求、目标…

Java基础语法---集合---ArrayList

ArrayList是什么 ArrayList可以看作是一个动态数组&#xff0c;提供了自动扩容的能力&#xff0c;意味着它能够根据需要自动调整其大小以容纳更多的元素&#xff0c;而无需预先指定数组的容量。 使用ArrayList需要加入包 import java.util.ArryList ArrayList与普通数组的不同…

Si3N4/SiC纳米复相陶瓷综合性能明显提升 下游可应用范围广泛

Si3N4/SiC纳米复相陶瓷综合性能明显提升 下游可应用范围广泛 Si3N4/SiC纳米复相陶瓷&#xff0c;是以碳化硅&#xff08;SiC&#xff09;纳米颗粒为第二相&#xff0c;弥散进入氮化硅&#xff08;Si3N4&#xff09;基体相制备得到的新型陶瓷材料&#xff0c;对碳化硅陶瓷具有强…

Cannot add ‘xxxxxx‘to Logic Analyzer

问题描述&#xff1a;Keil 中&#xff0c;直接切换到仿真中并添加变量到逻辑分析仪&#xff0c;会报如题类型错误。 解决方法&#xff1a; 需要在先在执行main函数&#xff0c;生成变量内容&#xff0c;然后在添加到逻辑分析仪。具体方法是&#xff0c;在mian 中打断点——运…

小程序简单版录音机

先来看看效果 结构 先来看看页面结构 <!-- wxml --><view class"wx-container"><view id"title">录音机</view><view id"time">{{hours}}:{{minute}}:{{second}}</view><view class"btngroup"…

169.二叉树:完全二叉树的节点个数(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr, right(nullptr) {}* Tree…

“墨者杯”网络安全大赛wp

漏洞利用01 504错误修改为POST提交拿到php源码&#xff0c; 查看逻辑$_POST[roam1] ! $_POST[roam2] && sha1($_POST[roam1]) sha1($_POST[roam2]) 采用数组绕过 roam1[]1&roam2[]2 拿到phpinfo&#xff0c;观察发现 这里的意思是每个php页面都包含这个f14…

微服务网关Gateway(上)

大家好呀&#xff0c;我是苍何。 这年头&#xff0c;大家都在开始卷简历了&#xff0c;我也看了很多同学的简历&#xff0c;其中有一个同学的简历&#xff0c;我印象最为深刻&#xff0c;他的项目经历中&#xff0c;写了自定义 Gateway 过滤器实现统计接口调用耗时&#xff0c…

力扣141. 环形链表

Problem: 141. 环形链表 文章目录 题目描述思路复杂度Code 题目描述 思路 定义快慢指针fast、slow&#xff0c;当fast ! null && fast.next ! null时fast每次走两步、slow走一步&#xff0c;当fast和slow相遇时&#xff0c;则说明存在环 复杂度 时间复杂度: O ( n ) O…

无锡哲讯携手SAP,赋能装备制造业数字化转型

在当今快速发展的工业4.0时代&#xff0c;装备制造业作为国民经济的重要支柱&#xff0c;正面临着前所未有的机遇与挑战。无锡哲讯智能科技有限公司凭借其深厚的行业经验和专业的SAP实施能力&#xff0c;为装备制造业提供全面的数字化解决方案&#xff0c;助力企业实现智能化、…

知识图谱的应用---智慧政务

文章目录 智慧政务典型应用 智慧政务 智慧政务即通过“互联网政务服务”构建智慧型政府&#xff0c;利用云计算、移动物联网、人工智能、数据挖掘、知识管理等技术&#xff0c;提高政府在办公、监管、服务、决策中的智能水平&#xff0c;形成高效、敏捷、公开、便民的新型政府&…

TPM仿真环境搭建

文章目录 背景及注意事项一、CMake二、m4三、GNU MP Library四、TPM_Emulator五、TSS协议栈&#xff08;trousers-0.3.14.tar.gz&#xff09;六、 tpm-tools七、查看是否安装成功八、测试 TPM环境&#xff08;需要开三个终端分别运行&#xff09;8.1 启动TPM &#xff08;第一个…

有关大学的搜题软件?六个不限次的公众号和软件分享啦 #其他#职场发展

有些同学虽然喜欢刷题&#xff0c;但是如果参考答案遗失、找不到参考答案&#xff0c;导致做好的题目无法校对&#xff0c;就会比较烦恼了。不过不用担心&#xff0c;今天就给大家分享一些超好用的搜题工具 1.彩虹搜题 这是个老公众号了 它不仅可以查到大学题目&#xff0c;…

工厂为什么需要各种看板

人眼天生对图像识别速度更快更准确&#xff0c;例如图形&#xff0c;颜色等。人们往往更易于通过视觉信息来获取和理解信息&#xff0c;可视化的看板在工厂管理中也是司空见惯。 那么工厂看板如何帮助企业实现降本、提质、增效&#xff1f; 1、生产计划和生产进度的管理&#…

arm开发板移植sshd

移植sshd 文章目录 移植sshd1、准备工作2、编译zlib3、编译openssl4、编译openssh5、其他旧版本6、部署测试7、多用户配置8、sshd_config示例 1、准备工作 准备openssh-9.5p1.tar.gz openssl-1.1.1w.tar.gz zlib-1.2.11.tar.gz 我在http://10.45.156.100/IG2100/IG2100.git …

09.1手工制作docker镜像-多服务ssh+nginx

手工制作docker镜像-多服务sshnginx 一个容器多个服务 基于centos6.9系统添加yum源与epel源 安装nginx、ssh服务 yum install nginx openssh-server -y因镜像系统为纯系统&#xff0c;没有root密码&#xff0c;所以需要配置密码 echo 123456 | passwd --stdin root注&#x…

kafka-消费者组(SpringBoot整合Kafka)

文章目录 1、消费者组1.1、使用 efak 创建 主题 my_topic1 并建立6个分区并给每个分区建立3个副本1.2、创建生产者发送消息1.3、application.yml配置1.4、创建消费者监听器1.5、创建SpringBoot启动类1.6、屏蔽 kafka debug 日志 logback.xml1.7、引入spring-kafka依赖1.8、消费…

IT闲谈-WEB前端主流三大框架

目录 一、Angular二、React三、Vue.js小结 前言 这里给大家简单介绍一下web前端框架&#xff1b;随着互联网技术的飞速发展&#xff0c;Web前端技术也在不断地演进和更新。目前&#xff0c;前端比较多的三大主流前端框架Angular、React和Vue.js&#xff0c;成为前端开发者的得…