面向对象编程思想:OOP | 类和对象知识
面向对象编程思想:OOP
什么是面向对象?
- 面向对象是一种编程思想,是前辈们,总结的经验,指导程序员如何编写出更好的程序
- 核心是对象,程序就是一系列对象的集合,程序员负责调度控制这些对象来交互着完成任务
案例: 1-把大象装进冰箱
- 面向过程:
- 2.装入大象
- 3.关闭冰箱
- 面向对象:
- 找个具备装大象的技能的对象
- 在面向对象中,程序员的角度发生改变,从具体的操作者变成了指挥者
注意: 对象不是凭空产生的,需要我们自己设计
案例: 2-西天取经
- 如来有一堆破书要传出来,他没有自己干,而是找了五个对象帮他去干
- 如来只要负责控制调度的对象即可
- 如果某个对象发生变化,也不会影响其他的对象,扩展性与灵活性强
案例: 3-曹操吟诗
- 需求1:喝酒吃肉,人生真爽
- 需求2:喝酒吃肉,人生几何
- 需求3:对酒当歌,人生几何
这种一直改需求的情况,将字拆成一个一个的,要用时拼起来就行
面向对象的三大优点:
- 1.扩展性
- 2.灵活性
- 3.重用性
面向对象的缺点:
- 1.程序的复杂度提高
- 2.无法准确预知结果
使用场景:
- 对扩展性要求较高的程序,通常是直接面向用户的,例如QQ与微信
注意点: 不是所有程序都要面向对象,得分析具体需求
面向过程编程思想:
关键的核心是过程,过程就是一步一步的执行步骤
面向过程优缺点:
- 优点: 逻辑清晰,复杂问题简单化,流程化
- 缺点: 扩展性差,可维护性差
使用场景:
- 对扩展性要求较低的程序
例如: 系统内核,git,计算器
类和对象:
下面是OOP中的最核心的两个概念:
-
类:
- 即类型,类别,是一种抽象概念
- 是一系列具备相同特征和行为的对象的集合
-
对象:
- 就是具体存在的某个事物,具备自己的特征与行为
- 对象就是特征和技能的结合体
类和对象的关系:
- 类包含一系列对象
- 对象属于某个类
- 在生活中是先有对象再有类
- 而在程序中是先有类才能有对象,我们必须先告诉计算机这类的对象有什么特征,有什么行为
总结一个结论:
- 在使用面向对象编程时,第一步就是思考需要什么样的对象,对象具备什么样的特征和行为,从而根据这信息总结出需要的类型
创建类和对象:
定义类的语法:
class 类的名称: # 类中的内容 描述属性和技能 # 描述属性用变量 # 描述行为用函数 # 类名称的书写规范: 首先是见名知意,名称是大驼峰命名法 # 驼峰就是单词首字母大写,大驼峰是第一个字母大写,小驼峰是第一个字母小写
类的定义:
class Student: pass # 创建对象的语法,调用类,即类名加括号 stu = Student() print(stu) # <__main__.Student object at 内存地址> print(Student) # <class'__main__.Studet'>
创建对象的语法:
class Person: pass # 创建对象 p = Person()
属性的写法:
- 属性可以写在类中类中的属性,是所有对象公共的
- 也可以写在对象中
- 对象中的数,每个对象独特的(不一样的)
- 如果类中和对象中存在同样的属性,先访问对象,如果没有,再访问类
练习: 描述一个老师类,需要包含一个公共属性和一个独特的属性
class Teacher: school = 'oldboy' t1 = Teacher() t1.name = 'jack' t1.age = 18 t2 = Teacher() t2.name = 'nick' t2.age = 28
属性的增删查改:
# 增加属性 # 对象变量名称.属性名称 = 属性值 # 删除属性 # del 对象的变量名称.属性名称 # 修改属性 # 对象.属性 = 新的值 # 查看属性(访问的是对象的所有属性) print(对象.__dict__) # __doc__是类的注释 print(对象.__class__) # 访问对象的类信息
实例:
class Person1: name = '朱质健' age = 88 sex = 'male' p1 = Person1() print(p1.name) # 朱质健 p2 = Person1() print(p2.name) # 朱质健 class Person2: desc = '都能吃饭' p3 = Person2() p3.name = 'bilibili' p4 = Person2() p4.name = 'bangbangbang' print(p3.name) # bilibili print(p4.name) # bangbangbang
初始化对象的属性:
class Teacher: school = 'oldboy' t1 = Teacher() t2 = Teacher() init(t1,'jason',20) init(t2,'tank',20) class Teacher: school = 'oldboy' def init(obj,name,age): obj.name = name obj.age = age def __init__(self,name,age) self.name = name self.age = age t1 = Teacher('jason',18) Teacher.__init__(t1,'jason')
__init__方法:
它叫做初始化方法,本质上就是一个函数
- 特点1:
- 当实例化对象时,会自动执行__init__方法
- 特点2:
- 会自动将对象作为第一个参数传入,参数名称为self,self也可以是别的名字,不建议改
功能:
- 用户给对象赋初始值
练习: 创建一个类,具备几个属性,通过初始化方法来给他设置属性
class Dog: def __init__(self,kind,color,age): self.kind = kind self.color = color self.age = age d1 = Dog('二哈','黑白',1) d2 = Dog('泰迪','棕色',2)
初始化方法不仅仅用于赋值:
class PC: def __init__(self,kind,price,sys): self.kind = kind self.price = price self.sys = sys # 除了赋值以外的初始化操作,例如:启动BIOS等 print('启动BIOS') print('启动系统分区') print('加载系统界面') print('启动成功>>>') return None # 这个函数不能有其他返回值,只能是None,规定如此
为对象定制行为:
class Student: school = 'oldgirl' def __init__(self,name,age,gender) self.name = name self.age = age self.gender = gender def study(self): print(self) def say_hi(self): print('hello i am a student! my name is %s'%self.name) stu1 = Student('jack',20,'male') stu2 = Student('rose',18,'female') stu1.say_hi() stu2.say_hi() Student.say_hi(stu1) # 等价于stu1.say_hi() print(type(Student.say_hi)) # <class 'function'> print(type.stu1.say_hi) # <class 'method'>
总结:对象的精髓就是将数据和处理数据的函数整合到一起,这样一来,拿到一个对象就同时拿到了需要同时处理数据的函数
对象的绑定方法:
默认情况下类中的方法都是对象绑定方法
其特殊之处在于:
- 当使用对象,调用该函数时会自动传入对象本身,作为第一个参数
- 当使用类名,来调用时他就是一个普通函数,有几个参数就得传几个参数
练习:写一个学生类,具备一个打招呼的技能,要能输出自己的名字信息
class Student: def __init__(self,name): self.name = name def say_hi(self): print('hello my name is $s'%self.name)
类的绑定方法:
类绑定方法用@classmethod来装饰
特殊之处:
- 不管用类还是对象调用,都会自动传入类本身,作为第一个参数
什么时候绑定给对象:
- 当函数逻辑需要访问对象中的数据时
什么时候绑定给类:
- 当函数逻辑需要访问类中的数据时
class OldBoyStudent: school = 'oldboy' def __init__(self,name): self.name = name @classmethod def show_school(cls):
print(cls) OldBoyStudent.show_school() # <class'__main__.OldBoyStudent'> print(OldBoyStudent) # <class'__main__.OldBoyStudent'>
非绑定方法:
或叫做静态方法,就是既不需要访问类的数据,也不需要访问对象的数据,而且它并不常用
语法:@staticmethod
class OldBoyStudent: school = 'oldboy' def __init__(self,name): self.name = name @staticmethod def print_hello(): print('hello world')
序列化对象:
练习:为学生类添加一个save方法,一个get方法
- save是将对象存储到文件中
- get是从文件中获取对象
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.dump(self, f) @staticmethod def get(name): with open(name, 'rb')as f: obj = pickle.load(f) return obj stu = Student('rose') stu.save() stu1 = Student('jack') stu1.save() obj = Student.get('rose') print(obj.name) # rose
对象之间交互练习:
需求设计王者荣耀中的英雄类,每个英雄对象可以对其他英雄对象使用技能
具备以下属性:
- 英雄名称,等级,血量和Q_hurt,W_hurt,E_hurt 三个属性,表示各技能的伤害量
具备以下技能:
- Q W E
- 三个技能都需要一个敌方英雄作为参数,当敌方血量小于等于0时角色死亡
代码实现(常规的简单方法):
import random import time class Hero: def __init__(self,name,level,blood,attack,q_hurt,w_hurt,e_hurt) # self.name = name # self.level = level # self.blood = blood # self.attack = attack # self.q_hurt = q_hurt # self.w_hurt = w_hurt # self.e_hurt = e_hurt lcs = locals() lcs.pop('self') self.__dict__.updated(lcs) def attack(self,enemy): enemy.blood -= self.attack print('%s对%s释放了普通攻击,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.attack, enemy_blood) if enemy.blood <= 0: print('%s被%s使用普通攻击击杀了'%(enemy.name,self.name)) def Q(self,enemy): enemy.blood -= self.q_hert print('%s对%s释放了Q技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.q_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用Q技能击杀了'%(enemy.name,self.name)) def W(self,enemy): enemy.blood -= self.w_hert print('%s对%s释放了w技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.w_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用W技能击杀了'%(enemy.name,self.name)) def E(self,enemy): enemy.blood -= self.e_hert print('%s对%s释放了E技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.e_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用E技能击杀了'%(enemy.name,self.name)) h1 = Hero('亚索',20,2000,200,600,0,50) h2 = Hero('妲己',20,2000,200,600,0,50) # 常规攻击方式 h1.attack(h2) h2.Q(h1) h2.W(h1) h2.E(h1)
英雄大乱斗(很有趣💗):
import random import time class Hero: def __init__(self,name,level,blood,attack,q_hurt,w_hurt,e_hurt) lcs = locals() lcs.pop('self') self.__dict__.updated(lcs) def attack(self,enemy): enemy.blood -= self.attack print('%s对%s释放了普通攻击,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.attack, enemy_blood) if enemy.blood <= 0: print('%s被%s使用普通攻击击杀了'%(enemy.name,self.name)) def Q(self,enemy): enemy.blood -= self.q_hert print('%s对%s释放了Q技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.q_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用Q技能击杀了'%(enemy.name,self.name)) def W(self,enemy): enemy.blood -= self.w_hert print('%s对%s释放了w技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.w_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用W技能击杀了'%(enemy.name,self.name)) def E(self,enemy): enemy.blood -= self.e_hert print('%s对%s释放了E技能,造成了%s伤害,敌人剩余血量%s'%(self.name, enemy.name, self.e_hurt, enemy_blood) if enemy.blood <= 0: print('%s被%s使用E技能击杀了'%(enemy.name,self.name)) h1 = Hero('亚索',20,2000,200,600,0,50) h2 = Hero('妲己',20,2000,200,600,0,50) h3 = Hero('鲁班',20,1500,500,200,200,50) h4 = Hero('蔡文姬',20,2000,100,800,100,100) # 从字典中随机拿出一个值 def random_hero(heros): hero_index = random.randint(1,len(heros)) return heros[hero_index] while True: # 将所有的攻击方法装进字典,为了随机取出一个 funcs = {1:Hero.Q,2:Hero.W,3:Hero.E,4:Hero.attack} func_index = random.randint(1,4) fun = funcs[func_index] # 将所有的英雄装进字典,为了随机取出一个 heros = {1:h1,2:h2,3:h3,4:h4} hero = random_hero(heros) # 剩余的英雄们 other_heros = {} new_index = 1 for k,v in heros.items(): if v != hero: other_heros[new_index] = v new_index += 1 # 从再次剩余的英雄中随机选取一个英雄来挨打 enemy = random_hero[other_heros] # 开始攻击 func(hero,enemy) if enemy.blood <= 0 break time.sleep(0,5)