面向对象编程思想 OOP
什么是面向对象:
面向对象是一种编程思想,核心是对象
程序就是一系列对象的集合,程序负责控制调度这些对象交互这完成任务
案例: 把大象装冰箱要几步?
面向过程思想:
1.打开冰箱
2.装入大象
3.关闭冰箱
面向对象思想:
找一个具备装大象功能的对象,让这个对象去工作
思维的转变,面向对象中,程序员从具体的操作者,变成指挥者
强调: 对象不会凭空产生,需要我们自己设计创建
优点:
1.扩展性强 (对象增加减少不影响其他对象运行)
2.灵活性
3.重用性
缺点:
1.程序的复杂程度提高了 (需要设计创建对象)
2.无法准确预知结果
使用场景
对扩展性要求较高的程序,通常是直接面对用户的
面向过程编程思想
什么是面向过程:
其核心是过程,针对结果分析过程,按步骤一步一步执行
优点:
逻辑清晰,复杂的问题简单化流程化.
缺点:
扩展性差(添加功能需要改动整体代码)
维护性差
使用场景:
对扩展性要求不高的程序:系统内核,计算器...
类和对象
OOP的两个核心概念
类
即一系列具有相同特征和行为的对象的集合
对象
是具体某个事物,具备自己的特征和行为,是特征和行为的集合体
类与对象的关系
类包含一系列对象,对象属于某个类
现实中:先有对象再有类
编程中:先有类再有对象,我们必须要先说明这类对象具有哪些特征和行为.
ps: 先定义(类),在使用(对象)
在使用面向对象编程时,第一步思考需要什么样的对象,对象具有什么样的特征和行为,第二步根据这些信息总结出需要的类型
创建类和对象
创建类的语法
class 类名: '''类中的内:特征和行为''' # 描述 特征(属性) 用 变量 # 描述 行为 用 函数 #类名的书写: 见名知意,大驼峰体即单词首字母大写
注意:
1.类中可以有任意的代码,这些代码在类定义阶段就会执行
2.类在定义阶段就会产生类的名称空间,用来存放变量和函数名,可以通过 类名.dict查看,返回的是一个字典
3.可以通过 类.变量名 或 对象.变量名 访问这些变量的值
创建对象的语法:
class 类名: # 特征(属性) # 行为 #创建对象,也成为实例化 obj = 类名()
注意: 实例化的对象有单独的名称空间,存对象放独有的属性
创建的对象
特征 (属性) 的写法
属性写在类中--->对象的公共属性
属性写在对象中--->对象的独有属性
若类中和对象中存在同样的属性,访问顺序: 先对象再到类
练习: 描述一个老师的类,包含一个公共属性,一个独特属性:
class Teacher: school = 'shanghai' obj = Teacher() obj.name = 'haha' # 相当于:obj.__dict__[name] = 'haha' print(obj.school) # >>> shanghai print(obj.name) # >>> haha
属性的增删改查
增加属性:
类中: 类名.属性名 = 值 # 类名.__dict__[属性名] = 值 对象中: 对象名.属性名 = 值 # 对象名.___dict__[属性名] = 值
删除属性:
del 对象.属性名 # 对象名.__dict__.pop('属性名')
修改属性
对象.属性 = 值 # 对象.__dict__[属性名] = 值
查看属性
对象.__dict__ # 查看的是对象中所有的属性 返回的是字典{'属性名':值} 类名.__dict__ # 查看的是类中所有的属性 返回的是字典{'属性名':值}
例:
class OldTeacher: func = 'give' def run(self): # 此函数在定义类的时候也会被定义出来,但只能通过类或对象调用才能执行 print('from run') print(OldTeacher) # 类的地址 <class '__main__.OldTeacher'> tea = OldTeacher() # 对象 # 生成一个属于OldTeacher类的对象,用tea接收,tea拥有这个类里的所有属性 print(tea) # <__main__.OldTeacher object at 0x00000123E8387470> # 曾 属性 tea.name = 'js' print(tea.run()) # >>> from run # 查 属性 print(tea.name) # >>> js print(tea.func) # >>> give print(OldTeacher.func) # give print(tea.__dict__) # 打印出对象中的属性和方法 是个字典 print(OldTeacher.__dict__) # 打印出类中的属性和方法 是个字典 print(tea.__class__) # <class '__main__.OldTeacher'> # 对象属于的类 print(OldTeacher.__class__) # <class 'type'> # 删 属性 del tea.func # 删除类中的属性 del tea.name # 删除对象中的属性 # 改 属性 tea.func = 'xx' # 修改属性
双下init 方法
即初始化方法,本质是一个函数
功能:
为对象初始化自己独有的属性特征,即用户给对象赋初始值
特点:
1.实例化对象时,才会执行双下init方法
2.会自动将对象作为第一个参数传入(默认self)
练习:创建一个类 具有几个属性,通过初始化方法给他们设置属性
class Dog: def __init__(self, kind, color, age): # 给对象赋初始值,初始值就是__init__函数形参对应的实参 self.kind = kind self.color = color self.age = age # 实例化对象,开始运行__init__函数,并传参 d1 = Dog('二哈', '黑白', 1) d2 = Dog('泰迪', '棕色', 1) # 简便写法 class Dog: def __init__(self, kind, color, age): lcs = locals() lcs.pop("self") self.__dict__.update(lcs) d1 = Dog('二哈', '黑白', 1) d2 = Dog('泰迪', '棕色', 1)
注意:
双下init函数只有在实例化是才会运行,默认参数是对象本身,且不能有返回值,只能返回None
对象的精髓就是将数据和处理数据的函数整合到一起,这样拿到一个对象就同时拿到了需要处理的数据以及处理数据的函数
例:
class Teacher: # 对象共有的属性 ps: 无论类调用还是对象调用属性,id都一样 func = 'give' school = 'shanghaisc' def __init__(self, name, age): # self 是 对象本身 self.name = name self.age = age print(self) # 实例化对象t 时,是: <__main__.Teacher object at 0x000002BB0A897A20> # 实例化对象t1 时,是: <__main__.Teacher object at 0x000002BB0A897AC8> print('from __init__') #实例化对象时才调用 __init__函数 , 一定不能有返回值 #Teacher() # 类名加括号,调用类 t = Teacher('x', 2) # 调用类 初始化对象(实例化对象) 并传参 t.weight = '50kg' print(t) # <__main__.Teacher object at 0x000002BB0A897A20> print(t.func) # give print(t.name) # x print(t.age) #2 print(t.weight) # 50kg t1 = Teacher('yy', 20) #调用类 实例化对象 并传参 print(t1) # <__main__.Teacher object at 0x000002BB0A897AC8> print(t1.func) print(t1.name) print(t1.age)
类与对象的名称空间
class Animal: f = '攻击' r = ['running'] def __init__(self, name, age, hp): self.name = name self.age = age self.hp = hp def eat(self): print('吃药回血') print(Animal.__dict__) dog = Animal('二哈', 2, 100) dog.function = '拆家' print(dog.f) # 攻击 dog.f = '咬' # 当对象点类中的不可变静态属性并赋了值,会在对象的名称空间中创建该属性及值 print(dog.f) # 咬 dog.r[0] = 'fly' # 当对象点类中的可变静态属性并赋了值,只是改变了该属性的值 print(dog.__dict__) # {'name': '二哈', 'age': 2, 'hp': 100, 'function': '拆家', 'f': '咬'}
对象的绑定方法
默认情况是下类中的方法(函数)都是对象绑定方法,方法(函数)默认传入参数self,即对象
对象调用与类调用的区别
对象调用该方法(函数)会自动将对象本身当实参传入,作为第一参数.
类调用时,这些方法就是一个普通函数,需要几个参数就传几个参数
练习: 写一个学生类,具备打招呼功能,要输出自己的名字
# 对象调用时,方法会把对象传入 class Student: def __init__(self, name): self.name = name # 实例化后生成对象的名称空间,name放入空间里 def say_hi(self): print("hello my name is %s" % self.name) # 对象经过实例化后生成对象空间 s = Student('haha') # 等同于 Student.say_hi(s) print(s.name) s.say_hi() # >>> hello my name is haha # 用类调用函数 class Student: def say_hi(self): print("hello my name is %s" % self) # 类调用方法时,方法就是一个普通函 Student.say_hi('YY') # >>> hello my name is YY
类绑定方法
语法:
@classmethod
def 函数名():
无论类调用还是对象调用,都会自动传入类本身,作为第一个参数
class Student: def __init__(self, name): self.name = name @classmethod def say_hi(self): print(self) # >>> <class '__main__.Student'> print(Student) # >>> <class '__main__.Student'> s = Student('YY') s.say_hi() Student.say_hi()
非绑定方法
语法 @staticmethod
即静态方法,既不需要访问类的数据,也不访问对象的数据
class Student: def __init__(self, name): self.name = name @staticmethod def say_hi(): print('from say_hi') s = Student('YY') s.say_hi() # >>> from say_hi Student.say_hi() # >>> from say_hi