面向对象-OOP
面向对象编程——Object Oriented Programming,简称
OOP
,是一种以对象为中心的程序设计思想。面向过程编程——Procedure Oriented Programming,简称
POP
,是一种以过程为中心的程序设计思想。
##1.面向过程与面向对象
###区别和优缺点:
-
面向过程:
遇到问题后,分析解决问题的步骤,然后一步步去实现;面向过程的核心是过程,过程就是指解决问题的步骤。
优缺点:
- 优点:将负责的问题流程化,进而实现简答化
- 缺点:扩展性差(更新,维护,迭代)
总结:在去完成一些简单的程序时,可以使用面向过程去解决。但是如果有复杂的程序或任务,而且需要不断的进行迭代和维护,那么肯定是优先选择面向对象的编程思想
-
面向对象:
通过分析问题中需要的抽象模型,然后根据需要的功能分别去创建模型对象,最终由模型对象来完成程序;面向对象的核心是对象,是一个特征和功能的综合体。
面向对象就是调用对象去解决问题,所以首先需要先建立出对象的抽象模型,就是建立类。然后对象(也就是在类中)通过面向过程来一步步去解决问题。但是对于我们来说,就是调用了对象,然后问题就被对象解决了。
优缺点:
- 优点:可扩展性高
- 缺点:编程的复杂度相对面向过程要高一些。指的是计算机在执行面向对象的程序时的性能表现。
###认识类与对象:
类:类是对象的抽象概念;
对象(实例):对象就是由类创建的实例;
类和对象的关系就是 模具与成品的关系:
- 类是由对象总结而来的,总结的这个过程叫做抽象;
- 对象是由类具体实施出来的,这个过程叫做实例化。
- 水果是类,具体的某种水果-比如苹果,就是一个对象;
- 手机是类,具体的某种手机-比如小米,就是一个对象;
- 笔记本电脑是一个具体的实实在在存在的东西,是一个对象。
##2.面向对象编程的基本实现
如果需要创建一个对象,那么需要先建立一个抽象的模型,就是类;
然后由类实例出对象
例如需要创建一个汽车对象:
首先:需要抽象一个汽车类,汽车类就相当于一个设计图纸一样;
然后:由设计图纸去创建(实例)出来的真实汽车,就是一个对象。
- 通过class关键字来定义一个类:
#定义一个汽车类
class Car():
pass
-
类名的书写规范,使用驼峰命名法:
''' 类名的书写规范,建议使用驼峰命名法 大驼峰:MyCar XiaoMi #每个单词的首字母大写 小驼峰:myCar xiaoMi #第二个单词的首字母大写 '''
-
类中需要声明的两个内容:
1. 属性:是一个变量,描述对象有什么特征 2. 方法:是一个函数,实现对象有什么功能 类中属性一般定义在前面,方法定义在后面
#定义一个汽车类Car class Car(): #定义属性:用变量来描述类的特征 color = '白色' brand = '奥迪' #定义方法:用函数来实现类的功能 def drive(self): print('可以开车')
-
通过类实例化对象并使用:
class Car(): #定义属性:用变量来描述类的特征 color = '白色' brand = '奥迪' #定义方法:用函数来实现类的功能 def drive(self): print('可以开车') #通过类实例化对象:调用类,此时a就是对象 a = Car() print(a) print(type(a)) >>><__main__.Car object at 0x000001638CB822C8> <class '__main__.Car'> #调用对象的方法 a.drive() print(a.drive()) >>>可以开车 None #【注意!这里的drive()函数里是print而不是return,所以直接a.drive()就可以输出,不能print(a.drive()),不然返回值是None】 #【怎么老是弄混这一点】 #调用对象的属性 print(a.color) >>>白色 【这里就属于在类的外部使用对象操作成员】
2.1成员属性和成员方法的操作
2.1.1. 对象操作成员:在类的外部进行
'''
什么是对象的成员?
一个对象通过类实例化之后,那么在类中定义的属性和方法,就叫做成员,可以使用实例化的对象进行操作;
类中定义的属性也称为 成员属性,
类中定义的方法也称为 成员方法;
成员属性的作用就是存储信息,供成员方法使用
'''
#定义一个Car类
class Car():
#定义属性:用变量来描述类的特征
color = '白色'
brand = '奥迪'
#定义方法:用函数来实现类的功能
def drive(self):
print('可以开车')
a = Car()
b = Car()
print(a)
print(b)
>>><__main__.Car object at 0x0000020019F72308>
<__main__.Car object at 0x0000020019F72608>
'''
一个类可以实例化多个对象;
以上的a和b变量都是对象,也都是通过Car这个类实例化出来的对象,
但是通过id地址可以看出,a和b是两个对象,它俩相同之处就在于都同由同一个类实例化出来。
'''
#####使用对象操作成员属性
-
访问对象成员属性:
先访问a对象自己的属性,如果没有就去访问这个对象的类的属性(也就是成员属性)
#通过对象访问类中的属性
print(a.color)
>>>白色
-
修改对象的成员属性:
实际上等于给当前对象创建了一个自己的color属性;
重新定义这个对象的color属性;
a.color = '黑色' print(a.color) print(b.color) #此时,b对象的color属性依然原来的值 >>>黑色 白色
-
添加对象的属性:
给当前对象创建了自己独有的name属性
#给当前a对象添加自己独有的属性 a.name = 'che' #此时b对象没有name属性
-
删除对象的属性:
只能删除这个对象自己的属性:给对象添加的属性和修改的属性,这个对象的类的属性不能删除
#只能删除这个对象自己的属性:给对象添加的属性和修改的属性 a.color = 'che' print(a.color) del a.color print(a.color) >>>che 白色 #删除修改的属性之后,对象的该成员属性值就变成了类里面的属性值 #注意!如果此时再del a.color就会报错,因为只能删除当前这个对象自己的属性,此时的color属性是属于类Car的。
#####使用对象操作成员方法
-
访问成员方法:
如果这个对象没有自己独立的方法,那么会访问这个对象的类的方法(也就是成员方法)
a.drive() >>>可以开车
-
修改对象的成员方法:
实际上等于给当前对象创建了一个自己的方法;
重新定义这个对象的成员方法;
def func(): print('这是一个新方法') a.drive = func #把一个函数赋值给成员方法 a.drive() #调用方法 >>>这是一个新方法 【注意!函数名后面带上括号就是调用,所以赋值的时候千万不要带括号!】
-
添加对象的方法:
给当前对象创建了自己独有的new方法
def func(): print('添加一个方法') a.new = func a.new() >>>添加一个方法
-
删除对象的方法:
只能删除这个对象自己的方法:给对象添加的方法和修改的方法,这个对象的类的方法不能删除
def func(): print('添加一个方法') a.drive = func #修改 a.new = func #添加 a.drive() a.new() del a.drive #删除修改 a.drive() del a.new #删除添加 #a.new() #这里此时会报错,因为已经没有这个方法了 #del a.drive #这里也会报错,因为不能删除对象的类的方法 >>>添加一个方法 添加一个方法 可以开车
'''
总结:
一个类定义了类成员属性和成员方法,那么通过这个类实例化的对象,也具备这些方法和属性吗?
实际上,创建对象的时候,并不会把类中的属性和方法复制一份给对象,而是在对象中引用父类的方法。
因此在访问对象的属性时,会先去找对象自己的属性,如果没有就去找这个类的属性和方法。
一个对象由类实例化以后,是一个独立的对象,会引用父类中的属性和方法;
如果在对象创建后,给对象的属性或方法,进行修改或添加,那么此时等于给这个对象创建了一个自己的属性和方法,
所以在删除时,只能删除对象被修改或添加的成员。
'''
'''
总结:
成员属性:
访问: 对象.成员属性名
修改: 对象.成员属性名 = 新值(此时等于给这个对象创建了一个自己的属性)
添加: 对象.新属性名 = 值 (此时是给这个对象自己新建了一个属性)
删除: del 对象.属性名 (注意:只能删除这个对象自己的属性)
成员方法:
访问: 对象.成员方法名()
修改: 对象.成员方法名 = func(此时等于给这个对象创建了一个自己的方法)
添加: 对象.新方法名 = func (此时是给这个对象自己新建了一个方法)
删除: del 对象.方法名 (注意:只能删除这个对象自己的方法)
'''
2.1.2 类操作成员(不推荐)
在类的外部,可以直接通过类对成员进行操作
#定义一个Car类
class Car():
#定义属性:用变量来描述类的特征
color = '白色'
brand = '奥迪'
#定义方法:用函数来实现类的功能
def drive(self):
print('可以开车')
#####类成员属性的操作
- 访问类成员属性:
print(Car.color)
>>>白色
-
修改类成员属性:
如果通过类把属性进行修改,那么再通过这个类实例化的对象,它的属性是会变成修改之后的值;
在类属性修改前创建的对象的属性也会变成修改之前的值。
a = Car() Car.color = '黑色' b = Car() print(Car.color) print(b.color) # 是修改后的结果 print(a.color) # 在类属性修改前创建的对象的属性也被修改 >>>黑色 黑色 黑色
-
添加类成员属性:
添加之前创建的对象和之后创建的对象都会有这个新的属性
a = Car() Car.zxc = '瞎写的' b = Car() print(Car.zxc) print(a.zxc) print(b.zxc) >>>瞎写的 瞎写的 瞎写的
-
删除类成员属性:
删除之前和之后创建的对象,都没有这个属性了
a = Car() Car.zxc = '瞎写的' b = Car() del Car.zxc del Car.color #可以删除类里面的属性 #此时a和b都没有这个属性了
#####类成员方法的操作
【同上】
'''
总结:
成员属性:
访问: 类名.成员属性名
修改: 类名.成员属性名 = 新值。(此时通过这个类创建的对象都具有这个属性)
添加: 类名.新成员属性 = 值 (此时通过这个类创建的对象都具有这个属性)
删除: del 类名.成员属性 (注意:删除这个类的属性后,这个类创建的对象也没有这几个属性了)
成员方法:
访问: 类名.成员方法名()
修改: 类名.成员方法名 = func(此时通过类创建的对象都被修改)
添加: 类名.方法名 = func (此时通过类创建的对象都被修改)
删除: del 类名.方法名 (注意:此时通过类创建的对象都被修改)
'''
####2.1.3.总结
- 一个类可以实例化出多个对象,每个对象在内存中都独立存在的
- 当通过类实例化对象时,并不会把类中的成员复制一份给对象,而去给对象了一个引用
- 访问对象成员的时候,如果对象自己没有这个成员,对象会向实例化它的类去查找
- 对象成员的添加和修改,都只会影响当前对象自己,不会影响类和其它对象
- 删除对象的成员时,必须是该对象自己具备的成员才可以,不能删除类中引用的成员
- 对类的成员操作,会影响通过这个类创建的对象,包括之前创建的。
2.2 成员方法中的self
- self在方法中只是一个形参,并不是关键字;
- self 单词本身的意思:自己;
- self 在类的方法中代表当前这个对象;
- self 代表调用这个方法的对象,谁调用了这个方法,self就代表谁;
- self 就可以在类的内部代替对象进行各种操作,要是对象能干的事,self都可以代表对象去完成;
- 如果成员方法中不含self形参,则该方法是绑定类方法,不能使用对象去调用,只能使用类去访问。
2.3 方法的分类
- 含有self或者可以接受对象作为参数的方法: 非绑定类方法
- 不含self或者不能接受对象作为参数的方法:绑定类方法
非绑定类方法,只能使用对象去访问,如果使用类去访问就会报错;
绑定类方法,只能使用类去访问。
###2.4 魔术方法
魔术方法也和普通方法一样都是类中定义的成员方法;
魔术方法不需要手动调用,魔术方法会在某种情况下自动执行;
魔术方法还有一个比较特殊的地方:多数的魔术方法前后都有两个连续的下划线;
魔术方法不是我们自己定义的,而是系统定义好的,我们来使用。
__init__
初始化方法:类实例化对象时自动触发
- 触发机制:在通过类实例化对象后,自动触发
- 作用:可以在对象实例化后完成对象的初始化(属性的赋值,方法的调用)
- 应用场景: 文件的打开,数据的获取等等干活前的一些准备功能
class Person():
# 成员属性
name = None
age = None
sex = None
# __init__ 初始化方法
def __init__(self,n,a,s):
# print('我是一个初始化方法')
# 完成对象属性的初始化
self.name = n
self.age = a
self.sex = s
# 调用方法
self.say()
# 成员方法
def say(self):
print(f'大噶好,唔系{self.name},系兄弟就来砍我。。')
# 实例化对象
# zzh = Person()
zzh = Person('渣渣灰',56,'男')
####__del__
析构方法:对象被销毁时自动触发
-
触发机制:在对象被销毁时,自动触发
-
作用:关闭一些开发的资源,比如在初始化方法中开发的文件,可以在析构方法中关闭
【注意!是对象被销毁时触发了析构方法,而不是析构方法销毁了对象】 -
对象会在哪些情况被销毁?
【按照以下顺序】
- 首先,如果使用了del删除【这里的首先也包括“del删除”在“对象不再被引用”之前!如果先是“对象不再被引用”,那么就先销毁这个对象】;
- 其次,对象不再被引用时【也就是类操作成员时】,会自动销毁【这里的其次也包括“对象不再被引用”在“del删除”之后!】;
- 最后,如果以上两种都不存在,则当程序执行完毕,内存中所有的资源都会被销毁释放
【如果这里不好理解,去看最后那个汽车类的例子!】
示例:
定义一个类,完成一个日志的记录
调用这个对象的时候,传递一个日志信息
这个对象会创建一个文件,开始写入,并在最后关闭这个文件
import time
class writeLog():
# 成员属性
# 文件的路径
fileurl = './'
# 日志文件的名称
filename = '2019-09-19'
# 初识化 打开文件
def __init__(self):
#完成文件的打开
print('初始化方法触发类。完成文件的打开')
self.fileobj = open(self.fileurl+self.filename,'a+',encoding='utf-8')
# 写日志的方法
def log(self,s):
print(f'把日志:{s} 写入文件中')
# 析构方法
def __del__(self):
print('析构方法触发了,关闭打开的文件')
# 在对象被销毁时,关闭在初始化方法中打开的文件对象
self.fileobj.close()
【注意以下程序的执行结果!】
#定义汽车类
class Cart():
brand = ''
def __init__(self,b):
self.brand = b
print(f'初始化方法被触发:创建{self.brand}汽车')
def __del__(self):
print(f'析构方法被触发:{self.brand}汽车已经销毁了')
【问题1:下面程序的执行结果】
bm = Cart('宝马')
bc = Cart('奔驰')
fll = Cart('法拉利')
【这种销毁对象的情况属于“当程序执行完毕,内存中所有的资源都会被销毁释放”】
【结果为:】
'''
1 宝马创建
2 奔驰创建
3 法拉利创建
4 宝马被销毁
5 奔驰被销毁
6 法拉利被销毁
'''
【问题2:下面程序的执行结果】
Cart('宝马')
Cart('奔驰')
Cart('法拉利')
【这种销毁对象的情况属于“对象不再被引用时,会自动销毁”】
【结果为:】
'''
1 宝马创建
2 宝马销毁
3 奔驰创建
4 奔驰销毁
5 法拉利创建
6 法拉利销毁
'''
【注意析构方法被触发的三种情况的顺序!】
bm = Cart('宝马')
bc = Cart('奔驰')
del bc
Cart('法拉利')
【先找手动del(前提是del在类操作成员前面),然后找对象不再被引用(就是类操作成员),最后是程序执行结束】
【结果为:】
'''
1 宝马创建
2 奔驰创建
3 奔驰销毁
4 法拉利创建
5 法拉利销毁
6 宝马销毁
'''
【如果代码是:】
bm = Cart('宝马')
bc = Cart('奔驰')
Cart('法拉利')
del bc
【那么结果就是:】
'''
1 宝马创建
2 奔驰创建
3 法拉利创建
4 法拉利销毁
5 奔驰销毁
6 宝马销毁
'''
练习:日志类的封装
日志类 class Mylog 功能: 能够随时写入一个日志信息 分析: 日志文件在什么地方? 默认在当前目录 日志的文件名是什么? 当前日期 2019-09-20.log 日志的格式是什么样的? 2019-09-20 12:12:12 接收的信息 属性:成员属性的作用就是存储信息,供成员方法来使用的 fileurl 日志文件的地址 filename 日志文件的名称 fileobj 打开的文件对象 方法:具体完成的一个功能的过程 __init__() ==> 初始化方法,完成对象的初始化,并打开文件 log() ==> 负责接受用给的日志信息,并写入到日志文件中 __del__() ==> 析构方法,在对象被销毁时,关闭打开的文件
import time
#定义一个日志类
class myLog():
#属性
#fileurl = './'
#filename = str(time.strftime('%Y-%m-%d')) + '.log'
filename = './封装日志类生成的文件.txt' #可以把上面俩直接合成这一个
fp = None
#初始化方法
def __init__(self):
#打开文件
self.fp = open(self.fileurl + self.filename, 'a+', encoding='utf-8')
#log()方法,s是给出的日志信息
def log(self,s):
#写入的时间
date = time.strftime('%Y-%m-%d %H:%M:%S')
s = date + ' ' + s + '\n'
#写入信息
self.fp.write(s)
#析构方法
def __del__(self):
#关闭文件
self.fp.close()
#实例化对象
a = myLog()
#调用方法
a.log('写的好!艰!难!')
a.log('大艺术家好!洗!脑!')
#这样就可以运行的时候写入
res = input('输入你要写的内容:')
a.log(res)
>>>输入你要写的内容:学不动了.jpg
此时文件的最后一行就多了:
2020-06-17 14:15:39 学不动了.jpg