面向对象编程
1.面向对象简介
❤️Python支持面向过程、面向对象、函数式编程等多种编程范
式。
- 面向对象和面向过程总结
- 都是解决问题的思维方式,都是代码组织的方式。
- 面向过程是一种“执行者思维",解决简单问题可以使用面向过程
- 面向对象是一种“设计者思维”,解决复杂、需要协作的问题可以使用面向对象
- 面向对象离不开面向过程:
- 宏观上:通过面向对象进行整体设计
- 微观上:执行和处理数据,仍然是面向过程
2.对象的进化
随着编程面临的问题越来越复杂,编程语言本身也在进化,从主要
处理简单数据开始,随着数据变多进化“数组”; 数据类型变复杂,
进化出了“结构体”; 处理数据的方式和逻辑变复杂,进化出了“对
象”。
3.类的定义
- 类:我们叫做
class
。 - 对象:我们叫做
object
,instance
(实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。 - 类和对象的关系比喻:我们把对象比作一个“饼干”,类就是制造这个饼干的“模具”。
4.属性和方法
- 简介
我们通过类定义数据类型的属性(数据、状态)和方法(行为),也就是
说,“类将行为和状态打包在一起”。
从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点:“方法代码是共享的,属性数据不共享”。
5.类对象
❤️Python中,“一切皆对象”。类也称为“类对象”,类的实例也
称为“实例对象”。
- 语法
要点:class 类名: 类体
- 类名必须符合“标识符”的规则;一般规定,首字母大写,多个单词使用“驼峰原则”。
- 类体中我们可以定义属性和方法
- 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作
- 类对象举例
class Student: def __init__(self, name, score): # 构造方法第一个参数必须为self self.name = name # 实例属性 self.score = score # 实例属性 def say_score(self): # 实例方法 print("{0}的分数是{1}".format(self.name, self.score)) s1 = Student('zhangsan', 80) # s1是实例对象,自动调用__init__()方法 s1.say_score()
- 类对象内存结构图
4.__init__()
构造方法和__new__()
方法
初始化对象,我们需要定义构造函数 __init__()
方法。构造方法用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值。
解释:相当于我们想要建一座房子,计划为这座房子买家具,瓷砖来装修。这里的家具,瓷砖要装修的就是__init__()
构造方法;这里的建的房子就是__new__()
方法。我们先建房子在装修,相当于我们先调用__new__()
方法来创建对象,接着用__init__()
构造方法初始化对象。
__init__()
要点:- 名称是固定的。必须为:
__init__()
。 - 第一个参数是固定的。必须为:
self
。self
指的就是刚刚创建好的实例对象。 __init__()
构造方法通常是用来初始化实例对象的实例属性的。如下代码就是初始化实例属性:name
和score
。def __init__(self,name,score): self.name = name #实例属性 self.score = score
- 通过
类名(参数列表)
来调用构造函数。调用后,将创建好的对象返回给相应的变量。比如:s1=Student('zhangsan',80)
。 __init__()
方法:初始化创建好的对象,初始化指的是:“给实例属性赋值”。__new__()
方法:用于创建对象,我们一般不需要重新定义该方法。- 如果我们不定义
__init__()
方法,系统会提供一个默认的__init__()
方法。如果我们定义了带参的__init__()
方法,系统不创建默认的__init__()
方法。
- 名称是固定的。必须为:
5.实例属性和实例方法,对象的其它操作
-
实例属性
- 实例属性是从属于实例对象的属性,也称为“实例变量”。他的使用有如下几个要点:
- 实例属性一般在
__init__()
方法中通过如下代码定义:self.实例属性名 = 初始值
- 在本类的其他实例方法中,也是通过
self
进行访问:self.实例属性名
- 创建实例对象后,通过实例对象访问:
obj01 = 类名()
# 创建和初始化对象,调用__init__()
初始化属性
obj01.实例属性名 = 值
# 可以给已有属性赋值,也可以新加属性
- 实例属性一般在
- 实例代码
class Student: def __init__(self,name,score): self.name = name #增加name属性 self.score = score #增加score属性 def say_score(self): self.age = 18 #增加age属性 print("{0}的分数是{1}".format(self.name,self.score)) s1 = Student("张三",80) s1.say_score() print(s1.age) s1.salary = 3000 #s1对象增加salary属性 s2 = Student("李四",90) s2.say_score() print(s2.age)
- 实例属性是从属于实例对象的属性,也称为“实例变量”。他的使用有如下几个要点:
-
实例方法
实例方法是从属于实例对象的方法。- 语法
def 方法名(self [, 形参列表]): 函数体
- 方法的调用格式如下:
对象.方法名([实参列表]
- 要点:
- 定义实例方法时,第一个参数必须为
self
。和前面一样,self
指当前的实例对象。 - 调用实例方法时,不需要也不能给
self
传参。self
由解释器自动传参- 实例对象的方法调用本质
- 实例对象的方法调用本质
- 函数和方法的区别
- 都是用来完成一个功能的语句块,本质一样。
- 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点
- 直观上看,方法定义时需要传递
self
,函数不需要
- 定义实例方法时,第一个参数必须为
- 语法
-
对象的其它操作
dir(obj)
可以获得对象的所有属性、方法obj.__dict__
对象的属性字典pass
空语句isinstance(对象,类型)
判断“对象”是不是“指定类型”
6.类对象、类属性、类方法、静态方法
- 类对象
我们在前面讲的类定义格式中,class 类名
: 实际上,当解释器执行class
语句时,就会创建一个类对象。 - 类属性
- 简介:
类属性是从属于“类对象”的属性,也称为“类变量”。由于,类属性从属于类对象,可以被所有实例对象共享。 - 语法:
class 类名: 类变量名 = 初始值
- 简介:
- 类方法
- 简介:
类方法是从属于“类对象”的方法。类方法通过装饰器@classmethod
定义。 - 语法:
@classmethod def 类方法名(cls [,形参列表]) : 方法体
- 注意:
@classmethod
必须位于方法上面一行- 第一个
cls
必须有;cls
指的就是“类对象”本身 - 调用类方法格式:
类名.类方法名(参数列表)
。 参数列表中,不需要也不能给cls
传值 - 类方法中访问实例属性和实例方法会导致错误
- 子类继承父类方法时,传入
cls
是子类对象,而非父类对象
- 简介:
- 静态方法
- 简介:
Python中允许定义与“类对象”无关的方法,称为“静态方法”。“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。静态方法通过装饰器@staticmethod来定义。 - 语法:
@staticmethod def 静态方法名([形参列表]) : 方法体
- 注意
@staticmethod
必须位于方法上面一行- 调用静态方法格式:
类名.静态方法名(参数列表)
- 静态方法中访问实例属性和实例方法会导致错误
- 简介:
7.__del__方法
(析构函数)和垃圾回收机制
- 简介
__del__()
称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__()
。我们也可以通过del
语句 删除对象,从而保证调用__del__()
。系统会自动提供__del__
方法 ,一般不需要自定义析构方法。 - 代码示例
#析构函数 class Person: def __del__(self): print("销毁对象:{0}".format(self)) p1 = Person() p2 = Person() del p2 # 先删除对象p2-->执行print("程序结束")-->引用对象由1变为0-->删除p1 print("程序结束") # 运行结果 销毁对象:<__main__.Person object at 0x02175610> 程序结束 销毁对象:<__main__.Person object at 0x021755D0>
8.__call__
方法和可调用对象
- Python 中,凡是可以将
()
直接应用到自身并执行,都称为可调用对象。 - 可调用对象包括自定义的函数、Python 内置函数、以及本笔记所说的实例对象。
- 定义了
__call__()
的对象,称为“可调用对象”,即该对象可以像函数一样被调用。 - 该方法使得实例对象可以像调用普通函数那样,以“
对象名()
”的形式使用。 - 代码示例:
def f1(): print("f1") f1() #本质也是调用了__call__()方法 class Car: def __call__(self, age,money): print("__call__方法") print("车龄:{0},金额:{1}".format(age,money)) f2 = Car() f2(3,200000) #像调用函数那样调用,本质也是调用了__call__() # 运行结果 f1 __call__方法 车龄:3,金额:200000
9. 方法没有重载
- 简介:
如果我们在类体中定义了多个重名的方法,只有最后一个方法有效。
10.方法的动态性
- 简介:
Python是动态语言,我们可以动态的为类添加新的方法,或者动态的修改类的已有的方法 - 代码示例:
我们可以看到,#测试方法的动态性 class Person: def work(self): print("天天向上!") def play_game(self): print("玩游戏") def work2(s): print("好好学习,天天向上!") Person.play = play_game Person.work = work2 p = Person() p.play() p.work()
Person
动态的新增了play_game
方法,以及用work2
替换了work
方法
11.私有属性和私有方法(实现封装)
- Python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有属性和私有方法,有如下要点:
- 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)。
- 类内部可以访问私有属性(方法)
- 类外部不能直接访问私有属性(方法)
- 类外部可以通过
_类名__私有属性(方法)名
”访问私有属性(方法)
- 注意:
⚠️【注】方法本质上也是属性!只不过是可以通过()执行而已。所以,此处讲的私有属性和公有属性,也同时讲解了私有方法和公有方法的用法。
12.@property
装饰器
- 简介:
@property
可以将一个方法的调用方式变成属性的调用方式。- 既要保护类的封装特性,又要让开发者可以使用“
对象.属性
”的方式操作操作类属性,除了使用property()
函数,Python 还提供了@property
装饰器。通过@property
装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对“()
”小括号。
- 语法
@property def方法名(self): 代码块
- 代码示例:
- 例如,定义一个矩形类,并定义用
@property
修饰的方法操作类中的price
私有属性,代码如下:
class Car: def __init__(self, price): self.__price = price @property # 相当于price属性的getter方法 def price(self): return self.__price car_p = Car(600000) #直接通过方法名来访问 price 方法 print("汽车的价格是:",car_p.price) # 运行结果 汽车的价格是:600000
- 上面程序中,使用 @property 修饰了
price()
方法,这样就使得该方法变成了price
属性的 getter 方法。需要注意的是,如果类中只包含该方法,那么price
属性将是一个只读属性。
也就是说,在使用Car
类时,无法对price
属性重新赋值,即运行如下代码会报错:
car_p = 800000 print("修改后的价格:",car_p.price) # 运行结果 Traceback (most recent call last): File "C:\Users\mengma\Desktop\1.py", line 10, in <module> car_p.price = 800000 AttributeError: can't set attribute
- 例如,定义一个矩形类,并定义用
13.setter
装饰器
- 接目录12而要想实现修改
price
属性的值,还需要为price
属性添加setter
方法,就需要用到setter
装饰器 - 语法:
@方法名.setter def 方法名(self, value): 代码块
- 代码示例:
- 例如,为
Car
类中的price
方法添加setter
方法,代码如下:
这样,class Car: def __init__(self, price): self.__price = price @property # 相当于price属性的getter方法 def price(self): return self.__price @price.setter # 相当于price属性的setter方法 def price(self, value): self.__price = value car_p = Car(600000) car_p = 800000 print("修改后的价格:",car_p.price) # 运行结果 修改后的价格:800000
price
属性就有了getter
和setter
方法,该属性就变成了具有读写功能的属性。 - 例如,为
14.deleter
装饰器
- 简介:
除上述12、13之外,还可以使用 deleter 装饰器来删除指定属性 - 语法:
@方法名.deleter def 方法名(self): 代码块
- 示例代码:
class Car: def __init__(self, price): self.__price = price @property # 相当于price属性的getter方法 def price(self): return self.__price @price.setter # 相当于price属性的setter方法 def price(self, value): self.__price = value @price.deleter def price(self): self.__price = 0 car_p = Car(600000) del car_p.price print("删除后的price值为:",car_p.price) # 运行结果 删除后的price值为:0
- 总结
@property
装饰器负责获取返回值,起读的作用。@方法名.setter
装饰器负责设置、处理、限制@property
获取的返回值,起写的作用。@方法名.deleter
装饰器负责属性的删除,起删的作用。
16.属性和方法命名总结
_xxx
:保护成员,不能用from module import *
导入,只有类对象和子类对象能访问这些成员。__xxx__
:系统定义的特殊成员__xxx
: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过 对象名. _类名__xxx 这种特殊方式访问Python不存在严格意义的私有成员)
⚠️再次强调,方法和属性都遵循上面的规则。
17.类编码风格
- 类名首字母大写,多个单词之间采用驼峰原则。
- 实例名、模块名采用小写,多个单词之间采用下划线隔开
- 每个类,应紧跟“文档字符串”,说明这个类的作用
- 可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类
18.关于None和判断的总结
None
是什么?- 与C和JAVA不同,python中是没有
NULL
的,取而代之的是None
。 None
是一个特殊的常量,表示变量没有指向任何对象。- 在Python中,
None
本身实际上也是对象,有自己的类型NoneType
。 - 你可以将
None
赋值给任何变量,但我们不能创建NoneType
类型的对象。 - ⚠️
None
不是False
,None
不是0,None
不是空字符串。None
和任何其他的数据类型比较永远返回False。
- 与C和JAVA不同,python中是没有
None
和其他类型的比较None
和其他任何类型比较都会返回False
a = None if a is None and a==None: print("a是None") #会执行 if a==False or a==0: print("None不等于False") #不会被打印
- 空列表、空字符串、1、0之间的比较
if
语句判断时,空列表[]
、空字典{}
、空元组()
、0
等一系列代表空和无的对象会被转换成False
a=[];b=();c={};d="";e=0;f=None if (not a) and (not b) and (not c) and (not d) and (not e) and (not f): print("if判断时,空列表[]、空字符串、0、None等代表空和无的对象会被转换成False")
==
和is
判断时,空列表、空字符串、空字典不会自动转成False
,是它们本来的类型。''' ==和is判断时,空列表、空字符串、空字典不会自动转成False ''' a = [] b = {} c = () d = '' e = 0 if (b == False or c == False or a == False or d == False): print('==判断时,空字典或空元组是False') # 不会执行 if (e == False): # 1是True 0是False print('==时,0会转成False') # ==时,0会转成False if (b is False or c is False or a is False or d is False): print('is判断时,空字典或空元组是False') # 不会执行 if (0 is False): # 1是True 0是False print('is时,0会转成False') # is时,0不会转成False
1
和0
在进行任何比较时,1
会被转换成ture
,0
会被转换成false
if 1: print("1在比较时会被转换为ture") if not 0: print("0在比较时会被转换为false")