面向对象(OOP)基本概念

前言

话说三国时期曹军于官渡大败袁绍,酒席之间,曹操诗兴大发,吟道:喝酒唱歌,人生真爽! 众将直呼:"丞相好诗",于是命印刷工匠刻板印刷以流传天下;

待工匠刻板完成,交与曹操一看,曹操感觉不妥,说道:"喝酒唱歌,此话太俗,应改为'对酒当歌'较好",于是名工匠重新刻板,当时还没有出现活字印刷术,如果样板要改,只能重新刻板,工匠眼看连夜刻版之工,彻底白费,心中叫苦不迭。可也只得照办。

版样再次出来请曹操过目,曹操细细一品,觉得还是不好,说”人生真爽太过直接,应该改问语才够意境,因此应改为‘对酒当歌,人生几何?’“,于是....

在活字印刷术还没出现之前,如果版样有改动,只能重新雕刻。而且在印刷完成后,这个样板就失去了它的价值,如果需要其他样板只能重新雕刻。而活字印刷术的出现就大大改善了印刷技术。如上例”喝酒唱歌,人生真爽“,如果用活字印刷,只需要改四个字就可,其余工作都未白做。岂不快哉!!

活字印刷也反应了OOP。当要改动时,只需要修改部分,此为 可维护;当这些字用完后,并非就完全没有价值了,它完全可以在后来的印刷中重复使用,此乃 可复用;此诗若要加字,只需另刻字加入即可,这就是 可扩展;字的排列可以横排,也可以竖排,此是 灵活性好。

曹操吟诗工匠刻录的故事(转载)

# 上述案列反应了面向对象的优点,即可维护性高,扩展性强,复用性高!
# 这些特点非常适用于用户需求变化频繁的互联网应用程序,这是学习面向对象的重要原因
# 但是面向对象设计的程序需涉及类与对象,相应的复杂度会提高!
#
# 并非所有程序都需要较高的扩展性,例如系统内核,一旦编写完成,基本不会再修改,使用面向过程来设计则更适用

小结

面向对象

面向对象: # 是一种编程思想,是前辈们总结的编程经验,指导程序员如何编写出更好的程序

核心: # 对象

程序就是一系列对象的集合,程序员负责调度控制这些对象来交互着完成任务 在面向对象中程序员的角度发生了改变,从具体的操作者变成了指挥者。

强调:对象不是凭空产生的,需要我们自己设计

面向对象的优缺点

'''
优点:
1.可扩展性高
2.灵活性高
3.重用性高 缺点:
1.程序的复杂度提高了
2.无法准确预知结果
'''

使用场景: # 对扩展性要求较高的程序,通常是直接面向用户的,例如qq 微信

注意点: # 不是所有的程序都要采用面向对象,要根据实际需求来选择

面向对象的两大核心: # 类与对象

'''
类即类型、类别,是一种 抽象概念
是一系列具备相同特征和相同行为的对象的集合
'''

对象

'''
对象就是具体存在的某个事物,具备自己的特征和行为
对象就是特征和技能的结合体
'''

类和对象的关系

'''
类包含一系列对象
对象属于某个类 在现实中先有对象再有类,程序中先有类再有对象
我们必须先告诉计算机这类的对象有什么特征有什么行为
'''

结论: # 在面向对象编程时,第一步需要考虑需要什么样的对象,对象具备什么样的特征和行为,从而根据这些信息总结出需要的类

用面向对象思想编程

类的定义语法及类名书写规范

class 类的名称:
# 类中的内容,描述属性和技能
# 描述属性用变量
# 描述行为用函数 '''
类名书写规范:
1.见名知意
2.名称是大驼峰式(驼峰就是单词首字母大写,大驼峰就是第一个字母大写,小驼峰是第一个字母小写)
3.其他规范与变量大致相同
'''

属性

'''
属性可以写在类中
类中的属性,是对象公共的 也可以写在对象中
对象中的属性,每个对象独特的(不一样的) 如果类和对象中存在同样的属性,先访问对象,如果没有再访问类
'''

属性的增删改查

'''
增加属性
对象变量名称.属性名称 = 属性值
egon.male = 'male'
删除属性
del 对象的变量名称.属性名称
del egon.male
修改属性
对象.属性 = 新的值
查看所有属性,访问的是对象的所有属性
对象.__dict__ --> dict 可以访问调用者自身的名称空间 访问他的类
对象.__class__ 返回类
'''
class Student:
'''
这是Student类的注释
'''
def __init__(self, name):
self.name = name class TeachOfOldBoy:
company = 'oldboy' def __init__(self, name):
self.name = name # ---------- 对象新增、修改、查看、删除属性 -------------
xuzhaolong = Student('xzl')
print(xuzhaolong.name) # 对象.属性 --> 访问对象的属性
# xzl
# print(xuzhaolong.age) # 访问不存在的属性会报错'Student' object has no attribute 'age'
xuzhaolong.age = 18 # 对象.属性 = 值 --> 为对象添加新属性
print(xuzhaolong.age)
#
xuzhaolong.age = 28 # 对象.属性 = 新值 --> 修改属性的值(如果该对象已有此属性)
print(xuzhaolong.age)
#
del xuzhaolong.age # 删除对象的属性
# print(xuzhaolong.age) # 会报错,属性已被删除,类中(属性的查找顺序:对象-->父类-->...如果还有父类,其他父类...-->Object)没有这个属性,AttributeError: 'Student' object has no attribute 'age' print(TeachOfOldBoy.__dict__) # __dict__查看调用者的名称空间里的名字
# {'__module__': '__main__', 'company': 'oldboy', '__init__': <function TeachOfOldBoy.__init__ at 0x0000026D1AC8A9D8>, '__dict__': <attribute '__dict__' of 'TeachOfOldBoy' objects>, '__weakref__': <attribute '__weakref__' of 'TeachOfOldBoy' objects>, '__doc__': None}
print(xuzhaolong.__dict__)
# {'name': 'xzl', 'age': 28} # ---------- __class__ 查看调用者所属类型(python里类也是一种数据类型) -------------
print(TeachOfOldBoy.__class__) # __class__ 查看调用者所属类
# <class 'type'>
print(xuzhaolong.__class__)
# <class '__main__.Student'> # ---------- __doc__ 查看调用者的注释 -------------
print(TeachOfOldBoy.__doc__) # __doc__ 查看调用者的注释
# None
print(Student.__doc__) # __doc__ 查看调用者所属类的注释
#
# 这是Student类的注释
#
print(xuzhaolong.__doc__) # __doc__ 查看调用者所属类的注释
#
# 这是Student类的注释
# # ------------ 类中的属性与对象 -----------------
egon = TeachOfOldBoy('egon')
print(egon.company)
# oldboy
egon.company = 'OldBoy'
print(egon.company) # 修改对象的继承的属性不影响类中的属性
# OldBoy
print(TeachOfOldBoy.company)
# oldboy

代码体现

属性的查找顺序: # 属性的查找顺序:先找自己的,再找类的,再父类。。。。一直往上找到object,再没有就报错

对象初始化__inint__

'''
__init__方法
特点
1.当实例化对象时,会自动执行__init__方法
2.会自动将对象作为第一个参数传入,对象参数名称为self,self这个名字可以改,但不建议(一眼就知道是对象本身了) 功能
用户给对象赋初始值
'''
# 初始化,不仅仅只是赋初值,还可以做一些其他操作,来保证对象的正常生成
'''
之前十个老师,每个老师都要输姓名年龄等,而属性的值又不一样,就不能直接定义成类的属性
每定义一个对象都要写一次,很麻烦 那么我们可以使用函数来简化这个赋值操作
class Cls: # 定义一个类
pass def init(obj, kind, color, age): # 定义一个方法来简化给对象添加(定制)属性
obj.kind = kind # 即 对象.属性 = 值(变量的) --> 像什么? 给对象添加属性嘛
obj.color = color
obj.age = age obj1 = Cls() # 实例化出一个对象
init(obj1, "泰日天", '棕色', 2) # 通过调用 init 函数来给该对象添加属性
obj2 = Cls()
init(obj2, "拆家犬", '黑白', 1)
# 这样子就有了两个对象,且都已经有了各自的属性了 ''' class Dog:
def __init__(self, kind, color, age):
# print(locals()) # {'age': 2, 'color': '棕色', 'kind': '泰日天', 'self': <__main__.Dog object at 0x000002985BE586D8>}
self.kind = kind
self.color = color
self.age = age
# __init__ 函数不允许写返回值(不能写,只能返回None,写return None 没有意思) 规定如此 '''
不过在类里面直接写这个 init 函数会更方便(python内部做了一些处理)
如上,当实例化对象时,会自动执行这个 __init__ 方法
会自动将调用这个类实例化的对象作为第一个参数传入,对象参数名称为self
''' # 那么在实例化的时候就可以简写成这样了,四行变两行
teidi = Dog("泰日天", '棕色', 2)
# print(teidi.__dict__) # {'kind': '泰日天', 'color': '棕色', 'age': 2}
erha = Dog("拆家犬", '黑白', 1) '''
其实上面的写法还可以有优化,那个__init__ 函数还可以写得更简洁一点(参数越多越明显)
def __init__(self, kind, color, age):
lcs = locals()
lcs.pop('self')
self.__dict__.update(lcs) 上面写法中的locals()内置函数在 __init__ 函数中,可以获取 __init__ 函数名称空间里的那些名字,它是一个字典
# print(locals()) # {'age': 2, 'color': '棕色', 'kind': '泰日天', 'self': <__main__.Dog object at 0x000002985BE586D8>}
我们将其赋给一个中间字典接收,将多余的 self 键值对去掉,就是我们对象想要的属性
而对象.__dict__正好也是一个字典
# print(teidi.__dict__) # {'kind': '泰日天', 'color': '棕色', 'age': 2}
都是字典?字典的update()方法还记得吗?
将这个中间字典update()到对象的.__dict__ 中,即完成了对象的属性添加
self.__dict__.update(lcs)
'''

__init__

备注:关于python内一些 __名字__ 这种属性是什么情况,可以参考这篇博客哦(都说不重要,但总有我这样的好奇宝宝嘛)~  Python常用内建方法:__init__,__new__,__class__的使用详解

绑定方法与非绑定方法

对象的精髓所在就是将数据和处理数据的函数整合到一起了,这样一来,拿到一个对象就同时拿到了需要处理的数据以及处理数据的函数

'''
******* 这一块基础概念是重点 *******
对象绑定方法:self --> 默认传入对象
默认情况下,类中的方法都是对象绑定方法 当使用对象调用时
其特殊之处在于调用该函数时会自动传入对象本身作为第一个参数
当使用类名来调用时就是一个普通函数,有几个参数就传几个参数 类绑定方法:@classmethod
cls(加上@classmethod装饰后自己将self改为cls(class是关键字)) --> 默认传入类
给类的抽象方法,默认传入类作为参数
特殊之处:不管用类还是对象调用,都会自动传入类本身,作为第一个参数 何时绑定给对象:当函数逻辑需要访问对象中的数据时
何时绑定给类:当函数逻辑需要访问类中的数据时 非绑定方法:@staticmethod
装饰成普通函数,不管谁调都一样
既不需要访问类的数据也不需要访问对象的数据
定义时有几个参数调用时就需要传入几个参数 '''
class Dog:
species = 'dog'
# 初始化方法 __init__ 也属于对象绑定方法
def __init__(self, kind, nickname, skill):
self.kind = kind
self.nickname = nickname
self.skill = skill # 这就属于一个对象绑定方法,默认将对象作为第一个参数传入(调用的时候不需要手动传入)
def introduce(self):
print(f"我是{self.kind},我能{self.skill},人送外号{self.nickname}") # 类绑定方法
@classmethod
def tell_species(cls): # 先写装饰器再写函数,默认参数就是cls了,写完函数再装饰的话,记得把self换成cls,表示参数是一个类
print(f"我的物种是{cls.species}") # 非绑定方法(既不需要传入类,也不需要传入对象)
@staticmethod
def normal_func(arg1, arg2):
print(f"参数一:{arg1} 参数二:{arg2},这只是一个普通方法啦~") taidi = Dog('泰迪', '泰日天', '日天')
erha = Dog('二哈', '拆家犬', '拆家') # --------绑定对象方法的调用
taidi.introduce()
erha.introduce()
# 我是泰迪,我能日天,人送外号泰日天
# 我是二哈,我能拆家,人送外号拆家犬
Dog.introduce(erha) # 类调用对象的方法要手动把对象传入进去
# 我是二哈,我能拆家,人送外号拆家犬 # --------类绑定方法调用
Dog.tell_species()
taidi.tell_species() # 可以直接调用,无需手动传入 class
# 我的物种是dog
# 我的物种是dog # --------非绑定方法调用
Dog.normal_func(1, 2) # 非绑定方法,定义时有几个参数,调用时就要传几个参数
taidi.normal_func(1, 2) # 非绑定方法,定义时有几个参数,调用时就要传几个参数
# 参数一:1 参数二:2,这只是一个普通方法啦~
# 参数一:1 参数二:2,这只是一个普通方法啦~

对象绑定方法、类绑定方法、非绑定方法

利用pickle模块将对象序列化到本地

如单机游戏的存档(把对象的数据等信息存储到文件中,下次启动再读取回来) --> 游戏扩展

import pickle

class Student:
def __init__(self, name):
self.name = name def say_hi(self):
print("name:", self.name) def save(self):
with open(self.name, "wb") as f: # pickle 模块操作文件必须是b 模式
pickle.dump(self, f) # 利用pickle 模块,将传入的对象序列化到了文件中(json模块不支持对象这种类型) @staticmethod
def get(name):
with open(name, "rb") as f:
obj = pickle.load(f) # 利用pickle 模块,将文件中的对象反序列化成python中的对象
return obj # 将rose 和 jack两个从内存中对象序列化到文件中
stu = Student("rose")
stu.save()
stu2 = Student("jack")
stu2.save() # 将他两反序列化出来到内存中
obj = Student.get("rose") # 调用类中的方法(类绑定方法)从文件中将对象加载到内存中
print(obj.name) # 使用该对象获取属性值
# rose
obj2 = Student.get("jack")
print(obj2.name)
# jack print(Student.__name__) # 获取类名
# Student # 反序列化出来的对象和序列化的那个对象已经不是同一个对象了
print(id(stu), id(obj))
# 2161209937816 2161209971880
print(stu.name, obj.name)
# rose rose
print(id(stu2), id(obj2))
# 2161209937872 2161209971824
print(stu2.name, obj2.name)
# jack jack
obj2.name = 'jack2'
print(stu2.name, obj2.name)
# jack jack2

将对象从内存中序列化反序列化到文件中

英雄大乱斗(随机)案例

import random
import time class Hero:
def __init__(self, name, health, attack, q_hurt, w_hurt, e_hurt):
lcs = locals()
lcs.pop('self')
self.__dict__.update(lcs) def attack(self, enemy):
print(f"-- {self.name} --使用普通攻击攻击了-- {enemy.name} --,造成了 {self.attack} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m")
enemy.health -= self.attack def Q(self, enemy):
print(f"\033[0m-- {self.name} --使用Q技能攻击了-- {enemy.name} --,造成了 {self.q_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m")
enemy.health -= self.q_hurt def W(self, enemy):
print(f"\033[32m-- {self.name} --使用W技能攻击了-- {enemy.name} --,造成了 {self.w_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m")
enemy.health -= self.w_hurt def E(self, enemy):
print(f"\033[35m-- {self.name} --使用E技能攻击了-- {enemy.name} --,造成了 {self.e_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m")
enemy.health -= self.e_hurt def check_hero(enemy, team):
if enemy.health <= 0:
print(f"\033[31m** {enemy.name} ** 阵亡。\033[0m")
if team == 'blue':
blue_team.remove(enemy)
elif team == 'red':
red_team.remove(enemy) def get_random_skill():
return skill_list.get(random.randint(1, 4)) def get_random_blue_hero():
return blue_team[random.randint(0, len(blue_team) - 1)] def get_random_red_hero():
return red_team[random.randint(0, len(red_team) - 1)] blue_team = [
Hero('瑞文', 465, 65, 30, 25, 70),
Hero('提莫', 300, 35, 50, 40, 60),
] red_team = [
Hero('李白', 320, 60, 35, 29, 77),
Hero('鲁班', 280, 79, 35, 40, 80),
] skill_list = {
1: Hero.attack,
2: Hero.Q,
3: Hero.W,
4: Hero.E,
} while len(red_team) > 0 and len(blue_team) > 0:
skill = get_random_skill()
blue = get_random_blue_hero()
red = get_random_red_hero()
flag = random.randint(0, 1)
if flag:
skill(blue, red)
check_hero(red, 'red')
else:
skill(red, blue)
check_hero(blue, 'blue')
time.sleep(0.3) if len(red_team) == 0:
print(f"蓝色方获胜!")
print(f"蓝色方所剩英雄状态为:")
for hero in blue_team:
print(f"{hero.name} 剩余生命值 {hero.health}")
elif len(blue_team) == 0:
print(f"红色方获胜!")
print(f"红色方所剩英雄状态为:")
for hero in red_team:
print(f"{hero.name} 剩余生命值 {hero.health}")

无注释版

注释版

import random
import time # 定义一个英雄类,表示英雄这一类的共同特征
class Hero:
def __init__(self, name, health, attack, q_hurt, w_hurt, e_hurt):
lcs = locals()
lcs.pop('self')
self.__dict__.update(lcs)
'''
********************************** 上述代码讲解 *************************************
# 每次调用类生成对象的时候都会执行这里面的代码,并将对象作为第一个参数self 传进来
print(locals())
# {'e_hurt': 70, 'w_hurt': 25, 'q_hurt': 30, 'attack': 65, 'health': 465, 'name': '瑞文', 'self': <__main__.Hero object at 0x000002371823B278>}
lcs = locals() # 这个locals()在 __init__ 函数里 可以获取 __init__ 函数名称空间里的那些名字,他是一个字典
lcs.pop('self') # 发现上面的 locals() 多了一个 self 是不需要的,那就把它删掉
print(lcs)
# {'e_hurt': 70, 'w_hurt': 25, 'q_hurt': 30, 'attack': 65, 'health': 465, 'name': '瑞文'}
# 这些内容正是初始化对象时想做的事(只不过是 对象.e_hurt = 70 这样的形式而已)
self.__dict__.update(lcs) # 将这些东西放到对象里,就重复了n变 self.参数 = 参数 (self.name = name)这样的动作
print(self.__dict__)
# {'e_hurt': 70, 'w_hurt': 25, 'q_hurt': 30, 'attack': 65, 'health': 465, 'name': '瑞文'} 最初始的写法:
self.name = name
self.health = health
self.attack = attack
self.q_hurt = q_hurt
self.w_hurt = w_hurt
self.e_hurt = e_hurt
''' def attack(self, enemy):
enemy.health -= self.attack
print(f"-- {self.name} --使用普通攻击攻击了-- {enemy.name} --,造成了 {self.attack} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m") def Q(self, enemy):
enemy.health -= self.q_hurt
print(f"\033[0m-- {self.name} --使用Q技能攻击了-- {enemy.name} --,造成了 {self.q_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m") def W(self, enemy):
enemy.health -= self.w_hurt
print(f"\033[32m-- {self.name} --使用W技能攻击了-- {enemy.name} --,造成了 {self.w_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m") def E(self, enemy):
enemy.health -= self.e_hurt
print(f"\033[35m-- {self.name} --使用E技能攻击了-- {enemy.name} --,造成了 {self.e_hurt} 点伤害,{enemy.name} 剩余 {enemy.health} 点生命值。\033[0m") def check_hero(enemy, team):
if enemy.health <= 0:
# \033[31m 这种格式的是打印时的颜色控制(颜色可参考 https://www.cnblogs.com/easypython/p/9084426.html)
print(f"\033[31m** {enemy.name} ** 阵亡。\033[0m")
if team == 'blue':
blue_team.remove(enemy)
elif team == 'red':
red_team.remove(enemy) # 随机选择一种攻击方式
def get_random_skill():
# random_index = random.randint(1, 4)
# random_skill = skill_list.get(random_index)
# return random_skill # 函数名当做返回值返回,拿到可以直接加括号调用执行函数
return skill_list.get(random.randint(1, 4)) # 上面代码的简便写法 # 随机选择一个蓝色方英雄
def get_random_blue_hero():
# 返回 blue_team 这个列表的索引为 random.randint(0, len(blue_team) - 1) 返回值的元素(英雄对象)
# return blue_team[random.randint(0, len(blue_team) - 1)] # 下面几行的简便写法
random_idndex = random.randint(0, len(blue_team) - 1)
hero = blue_team[random_idndex]
return hero # 随机选择一个红色方英雄
def get_random_red_hero():
return red_team[random.randint(0, len(red_team) - 1)] # 蓝色方英雄阵容 --- 可自定义 ---
# 方便随机数取英雄对象
blue_team = [
# 英雄名 生命值 普通攻击力 Q技能伤害 W技能伤害 E技能伤害
# 瑞文 465 65 30 25 70
Hero('瑞文', 465, 65, 30, 25, 70),
Hero('提莫', 300, 35, 50, 40, 60),
Hero('锤石', 600, 15, 20, 0, 32),
] # 红色方英雄阵容 --- 可自定义 ---
# 方便随机数取英雄对象
red_team = [
Hero('李白', 320, 60, 35, 29, 77),
Hero('鲁班', 280, 79, 35, 40, 80),
Hero('盾山', 800, 3, 3, 3, 3),
] # 技能数字对应表(方便根据随机数取技能)
skill_list = {
1: Hero.attack,
2: Hero.Q,
3: Hero.W,
4: Hero.E,
} def run():
while len(red_team) > 0 and len(blue_team) > 0:
# 调用方法随机获得一个技能
skill = get_random_skill()
# 调用方法随机获得一个蓝色方英雄
blue = get_random_blue_hero()
# 调用方法随机获得一个红色方英雄
red = get_random_red_hero()
# 随机选择一方为攻击方(那么另一方就是被攻击方)
flag = random.randint(0, 1)
if flag:
skill(blue, red)
check_hero(red, 'red')
else:
skill(red, blue)
check_hero(blue, 'blue')
# 暂停0.3秒,可以慢慢看战斗过程
time.sleep(0.3) # 如果有任意一方没有英雄了,即游戏结束
if len(red_team) == 0:
print(f"蓝色方获胜!")
print(f"蓝色方所剩英雄状态为:")
for hero in blue_team:
print(f"{hero.name} 剩余生命值 {hero.health}")
elif len(blue_team) == 0:
print(f"红色方获胜!")
print(f"红色方所剩英雄状态为:")
for hero in red_team:
print(f"{hero.name} 剩余生命值 {hero.health}") if __name__ == '__main__':
run()
'''
战斗记录
************************** 省略n多中间战斗步骤 *********************************
-- 锤石 --使用W技能攻击了-- 盾山 --,造成了 0 点伤害,盾山 剩余 42 点生命值。
-- 盾山 --使用E技能攻击了-- 瑞文 --,造成了 3 点伤害,瑞文 剩余 289 点生命值。
-- 瑞文 --使用E技能攻击了-- 盾山 --,造成了 70 点伤害,盾山 剩余 -28 点生命值。
** 盾山 ** 阵亡。
蓝色方获胜!
蓝色方所剩英雄状态为:
瑞文 剩余生命值 289
锤石 剩余生命值 235 战斗记录2
************************** 省略n多中间战斗步骤 *********************************
-- 盾山 --使用普通攻击攻击了-- 锤石 --,造成了 3 点伤害,锤石 剩余 11 点生命值。
-- 锤石 --使用普通攻击攻击了-- 盾山 --,造成了 15 点伤害,盾山 剩余 288 点生命值。
-- 盾山 --使用E技能攻击了-- 瑞文 --,造成了 3 点伤害,瑞文 剩余 -1 点生命值。
** 瑞文 ** 阵亡。
-- 盾山 --使用普通攻击攻击了-- 锤石 --,造成了 3 点伤害,锤石 剩余 8 点生命值。
-- 盾山 --使用普通攻击攻击了-- 锤石 --,造成了 3 点伤害,锤石 剩余 5 点生命值。
-- 盾山 --使用W技能攻击了-- 锤石 --,造成了 3 点伤害,锤石 剩余 2 点生命值。
-- 盾山 --使用Q技能攻击了-- 锤石 --,造成了 3 点伤害,锤石 剩余 -1 点生命值。
** 锤石 ** 阵亡。
红色方获胜!
红色方所剩英雄状态为:
盾山 剩余生命值 288
''' '''
有红蓝两方英雄(可自定义个数)
随机一方英雄使用随机攻击方式攻击另一方英雄,任意一方英雄全部阵亡则游戏结束
每个英雄有 名字、生命值、普通攻击、Q技能攻击、W技能攻击、E技能攻击以及对应的伤害值
当生命值为 0 时阵亡,不再参与战斗
'''

注释版案例

后续完善博客思路

调用类生成对象发生的一些事情(对比变量、模块名称空间生成等)

属性的查找顺序(画图表示吧)
  先找自己的,再找父类的,一级一级往上找,直到基类object,再没有报错 英雄大乱斗案例
序列化、反序列化对象(保存、读取对象)(游戏退出,读档)

待完善内容(不算是面向对象的重点啦)

python面向对象基础-01的更多相关文章

  1. java基础学习05(面向对象基础01)

    面向对象基础01 1.理解面向对象的概念 2.掌握类与对象的概念3.掌握类的封装性4.掌握类构造方法的使用 实现的目标 1.类与对象的关系.定义.使用 2.对象的创建格式,可以创建多个对象3.对象的内 ...

  2. 081 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 06 new关键字

    081 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 06 new关键字 本文知识点:new关键字 说明:因为时间紧张,本人写博客过程中只是 ...

  3. 080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则

    080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则 本文知识点:单一职责原则 说明:因为时间紧张,本人写博客过程中只是 ...

  4. 079 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 04 实例化对象

    079 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 04 实例化对象 本文知识点:实例化对象 说明:因为时间紧张,本人写博客过程中只是对知 ...

  5. 078 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 03 创建类

    078 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 03 创建类 本文知识点:创建类 说明:因为时间紧张,本人写博客过程中只是对知识点的关 ...

  6. 077 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 02 类和对象

    077 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 02 类和对象 本文知识点:类和对象 说明:因为时间紧张,本人写博客过程中只是对知识点 ...

  7. 076 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 01 Java面向对象导学

    076 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 01 Java面向对象导学 本文知识点:Java面向对象导学 说明:因为时间紧张,本人 ...

  8. Python 面向对象 基础

    编程范式概述:面向过程 和 面向对象 以及函数式编程 面向过程:(Procedure Oriented)是一种以事件为中心的编程思想. 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现 ...

  9. python面向对象基础

    面向对象基础 1. 简述 编程方式: 面向过程: 根据代码在脚本的堆叠顺序,从上到下依次执行 函数式编程:将相同功能的代码封装到函数中,直接调用即可,减少代码重复性 面向对象:对函数进行分类和封装,将 ...

随机推荐

  1. Dp优化之决策单调栈优化

    证明:g(i) ≤ g(j)   (i ≤ j) 令 d=g(i) , k<d , 设cut = x表示 f(i) = f(x) + w[x,i]    ( x < i ) 构造一个式子: ...

  2. Ajax 是什么? 如何创建一个Ajax?

    ajax的全称:Asynchronous Javascript And XML. 异步传输+js+xml. 所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他 ...

  3. SpringBoot中的异常处理方式

    SpringBoot中有五种处理异常的方式: 一.自定义错误页面 SpringBoot默认的处理异常机制:SpringBoot默认的已经提供了一套处理异常的机制.一旦程序出现了异常SpringBoot ...

  4. spring (反射+代理+DI+AOP)

    spring  https://baijiahao.baidu.com/s?id=1620606848227713760&wfr=spider&for=pc 反射 https://bl ...

  5. Ubuntu + Apache2 环境下用C编写 一个简单的cgi脚本

    我只学习过c语言,没有学习过prel,网上很多教程都是针对prel的,很少有针对c的.自己在Ubuntu下鼓捣了一下午,也总算是用c成功编写了一个helloworld的cgi,算是cgi入门的第一步. ...

  6. 阶段5 3.微服务项目【学成在线】_day17 用户认证 Zuul_05-用户认证-认证服务查询数据库-调用查询用户接口

    用户认证服务调用根据账号查询用户的信息 怎么远程调用呢?要创建一个客户端,这个客户端其实就是一个接口 标明服务的名称是ucenter服务 这是ucenter服务里面 复制过来接口的定义,GetMapp ...

  7. Linux -- 在多线程程序中避免False Sharing

    1.什么是false sharing 在对称多处理器(SMP)系统中,每个处理器均有属于自己的本地高速缓存区. 如图,CPU0和CPU1有各自的本地高速缓存区(cache).线程0和线程1会用到不同的 ...

  8. shutter 安装和设置快捷键

    1. 打开系统设置 2. 打开 Keyboard 键盘设置 3. 添加成功的状态 4. 单击右侧 Disabled,然后快速按下 Ctrl+Alt+A 如下图 5. Ctrl+Alt+A 测试OK. ...

  9. sqlserver如何创建链接服务器

    遇到下列问题: 线上服务器A,中转服务器B,本地服务器C 数据在A上面,想在B上面操作类似 select * from [A].[database].table这样的SQL,不用去链接服务器,直接把处 ...

  10. webpack简单配置

    1.代理配置 需要修改一下配置文件 config里的index.js,根据接口特点自主选取 2.解决图标显示路径错误问题 项目在打包完成后如果出现图片显示不了的问题,需要进行如下配置