面向对象
什么是面向对象
面向对象是一种编程思想,编程思想指的是人们利用计算机来解决实际问题的一种思维方式。与之相对的还有一种常见的编程思想,下面我们将详细介绍这两种编程思想,并对二者做出比较。要注意的是python同时支持面向过程和面向对象。
面向过程
概述:面向过程是一种以过程(函数)为中心的编程模型,核心思想是将程序分解为一系列的步骤去执行。关注的是“怎么做”。
我们解决问题时,会把所需要的步骤都列出来,然后按照步骤写代码挨个实现,这种过程化的叙事思维,就是面向过程思维。
举个例子:当我们用银行卡在ATM机上取钱有以下的步骤:带上银行卡到ATM机面前——>排队等待——>轮到自己,将银行卡插入ATM机——>输入密码,取钱——>收回银行卡,回家!这就是一个面向过程的实际例子
面向对象
概述:面向对象是一种以对象为中心的编程模型,核心思想是将程序中的数据(属性)和操作(行为)封装到一个类中,通过对象之间的交互进行执行。关注的是“谁来做”
当我们的视角不再是步骤过程,而是另一个视角:操作对象,这里的对象可以理解为:冰箱、手机、电脑等一切现实实体事物;这时候,我们就是使用面向对象的思维看待问题了。(补充一句:万物皆对象!)
同样举个例子:如果我们想要打扫房间,按照面向过程的思想我们就应该一步步的去实现打扫房间的过程,而按照面向对象的思想我们就可以考虑直接让扫地机器人进行打扫或者直接请专门的保洁人员进行打扫,我们考虑的不再是怎么完成这个任务,而是让谁来完成任务,具体过程可以忽略,只要最终的结果实现就好了。
面向对象的优点
- 使人们的编程与实际的世界更加接近,所有的对象被赋予属性和方法,这样编程就更加人性化;
- 宗旨在于模拟现实世界
- 在现实生活中,所有事物全被视为对象
虽然面向对象优点多,然而不是所以情况下都应该选择面向对象,当程序要求高性能而硬件资源受限时应当选择面向过程,在编写快速脚本工具时也应以面向过程优先,而在数学计算与算法原型中,因为面向过程会显得更加简洁也优先考虑面向过程。
面向对象编程基础概念
-
类与对象的定义与关系
- 类指的是一系列属性和方法的集合,抽象的概念,是看不见、摸不着的,相当于一个模版,可以基于这个类创建任意多个对象。
- 对象是类的具体实现(体现),对象是由类创建而来。
- 总结一句话:类是对现实事物的抽象概述,对象是现实事物的具体表现
- 三大特性:封装、继承、多态
- 实例属性与类属性的区别
Python 类的定义与使用
类的定义
# 三种定义类的方式
class 类名:
pass
class 类名():
pass
class 类名(object):
pass
注意:可以使用任意一种方式创建类,如果小括号中写上了具体的类名,意味着继承自该类
基于类创建对象以及调用类中的方法
基于类创建对象:对象名 = 类名()
调用类中的方法:对象名.成员()
class Car:
def run(self):
print("汽车正在运动。。。")
if __name__ == '__main__':
car = Car()
car.run()
self关键字
在上面的示例代码中我们能看到类内函数带有一个参数self,这是调用对象本身的关键字,是python内置的关键字,用于指向对象实例本身,一个类可实例化多个对象,用于区分哪个对象调用了类的方法。
class Car:
# 行为,定义了跑的功能,是一个动词
def run(self):
print(f"self:{self}")
print("汽车正在运动。。。")
if __name__ == '__main__':
# 2: 创建对象(基于类)
car = Car()
my_car = Car()
# 3: 适用对象,调用类中的成员
car.run() # self:<__main__.Car object at 0x0000026DDDCBEA50>
my_car.run() # self:<__main__.Car object at 0x0000026DDDF06910>
print(car) # <__main__.Car object at 0x000001F73DBFEA50>
添加获取对象的属性
设置/添加属性:方式1:类外,对象名.属性 = 属性值
方式2:类内,__init__()
获取属性:方式1:类外,对象名.属性名
方式2:类内,self.属性名
类外获取属性
class Car:
# 定义类的行为(方法)
def run(self):
print("汽车会跑...")
if __name__ == '__main__':
# 创建对象
car = Car()
my_car = Car()
# 设置属性
car.color = "红色"
car.num = 4
# 获取属性并打印输出
print(f"车辆的颜色:{car.color}, 车胎的数量:{car.num}")
my_car.color = "黑色"
my_car.num = 6
# 如果打印输出my_car的属性color和num,会报错:
# AttributeError: 'Car' object has no attribute 'color'
# 需要对my_car对象进行属性设置
print(f"车辆的颜色:{my_car.color}, 车胎的数量:{my_car.num}")
类内获取属性
class Car:
# 1.1 定义run()函数
def run(self):
print("汽车开始运行...")
# 1.2 定义show()函数
def show(self):
print(f"汽车颜色:{self.color}, 汽车轮胎数量:{self.number}")
if __name__ == '__main__':
# 2:创建对象
car = Car()
# 调用类中的成员函数
car.run()
# 3:给对象设置属性值
car.color = "红色"
car.number = 4
car.show()
# 4:创建一个新的对象
car2 = Car()
car2.show()
__init__
方法与实例初始化
补充:魔法方法:在python中,有一类方法是用来对python类的功能进行加强(扩展)操作的,并且这一类的方法不需要手动触发(自动触发),在某些特定场景,会被自动的触发,因此被称为魔法方法。
常见的魔法方法有:__init__、__str__、__del__
__init__ :用于初始化对象的属性值,在创建对象时,会被自动调用
__str__:用于快速打印对象的各个属性值,在输出语句打印对象时会被自动的触发
__del__:用于清除对象,当对象被删除或者文件执行完毕后,会自动调用
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
self.sex = 男
def examination(self):
print(f"{self.name}参加考试")
def __str__(self):
return f"学生姓名{self.name},今年{self.age}岁,性别{self.sex}"
def __del__(self):
print("当前对象已被删除")
if __name__ = '__main__':
student = Student("张三",18)
print(student)
del student
- 实例方法与类方法(
@classmethod
)、静态方法(@staticmethod
)
封装与访问控制
封装概述:将属性和方法书写到类的里面的操作即为封装,封装可以为属性和方法添加私有权限。
- 私有属性与方法的命名约定(
_
和__
)- 私有属性和方法的定义:在Python中,可以为属性和方法设置私有权限,即设置某个属性或方法不继承给子类。设置私有属性和方法的方式:在属性或方法名前加上__,格式:
# 私有属性
__属性名
# 私有方法
def __方法名():
...
私有属性和方法使用规则:只能在类的内部使用,不能在类的外部使用;如果想在类的外部使用通过公共接口
- 使用
property
装饰器实现属性访问控制 - 通过方法暴露数据(Getter/Setter 模式)
- 在Python中,一般定义函数名“get_xx”用来获取私有属性,定义“set_xx”用来修改私有属性。
class Person:
def __init__(self,name):
self.name = name
self.__money = 1000
# 定义公共的访问方式(接口),查看
def get_money(self):
print(f"获取到余额为{ self.__money}")
# 定义公共的访问方式(接口),修改
def set_money(self,money):
self.__money += money
if __name__ == '__main__':
person = Person("小明")
person.set_money(500)
person.get_money()
私有属性的意义:明确的区分内外,控制外部对隐藏的属性的操作行为
私有方法的意义:简化调用代码,降低程序复杂度
继承与多态
继承:一个类默认拥有了另一个类的公共属性和公共方法,这就是继承
补充:子类和父类的关系:
父类有一个object,它是所有类的祖宗类或者叫顶级类,也可以叫最原始的基类
子类和父类的关系:子类继承了父类(继承关系);父类派生了子类(派生关系)
普通父类也可以成为基类
子类又叫派生类,扩展类
- 单继承与多继承语法(
super()
的使用)- 单继承:格式:
class 子类名(父类名):
pass
注意:如果仅仅只有继承,没有其他操作,那这个继承没有意义
- 方法重写(Override)与
super()
调用父类方法
重写和扩写
重写:子类中把父类已经有的功能重新再子类中又写了一遍且名称相同
扩写:在子类中写了自己特有的功能
如果重写了父类功能子类调用同名功能时,默认调用的是子类自己的功能
如果重写后还想调用父类的同名功能,可以调用super()方法
重写后调用父类方法:
super(当前子类名,self).父类方法名()
# 简写
super().父类方法名()
# 也可以直接调用
父类名.父类方法名(self)# 类内
父类名.父类方法名(子类对象) # 类外
多继承:格式:
class子类名(父类名1,父类名2...):
pass
注意:多继承的时候super()默认代表的是父类1
可以用 类名.mro() 或者 类名.__mro__查看继承关系,方便判断执行顺序
- 多态的实现(鸭子类型与抽象基类
abc
模块)
多态:同一种事物做同一件事表现不同的形态,这就是多态
多态的三要素:有继承,有重写,父类引用指向子类对象(父类变量 = 子类名())
注意:python中的多态是伪多态:同一个函数传人不同的对象,实现不同的结果,这就是python中的多态,python是动态类型语言或者弱类型语言,默认不会限制对象类型。如果要让python实现类型限制,可以手动添加isinstance()进行类型判断
其他补充
- 类属性的添加和获取
在类外:
设置属性值:类名.类属性名 = 值
获取属性值:类名.类属性名 或者 对象名.类属性名
在类内:
设置属性值:直接在类中方法外直接写:类属性 = 值 或者 cls.类属性名 = 值
获取属性值: cls.类属性名 或者 self.类属性名
注意:对象或者self能获取类属性但不能修改类属性
- 类方法
经常用于和类属性配合使用
通过加@classmethod装饰器,变成类方法
注意:类方法的第一个形参必须是类对象,默认类对象名是cls
类方法相比实例方法多了一种调用方式 :类名.类方法名()
- 静态方法
通过加@staticmethod装饰器,变成静态方法
注意:静态方法不需要传人实例对象self也不需要传人类对象cls
静态方法相比实例方法也是多了一种调用方式:类名.静态方法名()