内容正文:
项目十 类与对象高级应用
Contents
类方法和静态方法
__slots__和@property装饰器
元类的用法
单例模式
类方法
PART 01
类方法是指使用@classmethod修饰的方法,其第一个参数是类本身(而不是类的实例对象)。
类方法的特点是既可以通过类名直接调用,也可以通过类的实例对象调用。
类方法
4
类方法
【任务描述】
使用@classmethod修饰
【关键步骤】
使用@classmethod修饰
示例01
代码缩进 4个空格
正文缩进1.27厘米
5
1 class Complex: #定义Complex类
2 def __init__(self,real=0,image=0): #定义构造方法
3 self.real=real #初始化一个复数的实部值
4 self.image=image #初始化一个复数的虚部值
5 @classmethod
6 def add(cls,c1,c2): #定义类方法add,实现两个复数的加法运算
7 print(cls) #输出cls
8 c=Complex() #创建Complex类对象c
9 c.real=c1.real+c2.real #实部相加
10 c.image=c1.image+c2.image #虚部相加
11 return c
12 if __name__=='__main__':
13 c1=Complex(1,2.5)
14 c2=Complex(2.2,3.1)
15 c=Complex.add(c1,c2) #直接使用类名调用类方法add
16 print('c1+c2的结果为%.2f+%.2fi'%(c.real,c.image))
类方法使用示例:
类方法
示例01
代码缩进 4个空格
正文缩进1.27厘米
6
<class '__main__.Complex'>
c1+c2的结果为3.20+5.60i
程序执行完毕后,将在屏幕上输出如下结果:
类方法
示例01
代码缩进 4个空格
正文缩进1.27厘米
7
静态方法
PART 02
静态方法是指使用@staticmethod修饰的方法。
与类方法相同,静态方法既可以直接通过类名调用,也可以通过类的实例对象调用。
与类方法不同的地方在于,静态方法中没有类方法中的第一个类参数。
静态方法
代码缩进 4个空格
正文缩进1.27厘米
9
静态方法
【任务描述】
使用@staticmethod修饰
【关键步骤】
使用@staticmethod修饰
示例02
代码缩进 4个空格
正文缩进1.27厘米
10
1 class Complex: #定义Complex类
2 def __init__(self,real=0,image=0): #定义构造方法
3 self.real=real #初始化一个复数的实部值
4 self.image=image #初始化一个复数的虚部值
5 @staticmethod
6 def add(c1,c2): #定义类方法add,实现两个复数的加法运算
7 c=Complex() #创建Complex类对象c
8 c.real=c1.real+c2.real #实部相加
9 c.image=c1.image+c2.image #虚部相加
10 return c
11 if __name__=='__main__':
12 c1=Complex(1,2.5)
13 c2=Complex(2.2,3.1)
14 c=Complex.add(c1,c2) #直接使用类名调用类方法add
15 print('c1+c2的结果为%.2f+%.2fi'%(c.real,c.image))
静态方法使用示例:
静态方法
示例02
代码缩进 4个空格
正文缩进1.27厘米
11
c1+c2的结果为3.20+5.60i
程序执行完毕后,将在屏幕上输出如下结果:
静态方法
示例02
代码缩进 4个空格
正文缩进1.27厘米
12
动态扩展类与实例
PART 03
Python作为一种动态语言,除了可以在定义类时定义属性和方法外,还可以动态地为已经创建的对象绑定新的属性和方法。
动态扩展类与实例
代码缩进 4个空格
正文缩进1.27厘米
14
动态扩展类与实例
【任务描述】
为对象动态绑定对象和方法
【关键步骤】
为对象动态绑定对象和方法
示例03
代码缩进 4个空格
正文缩进1.27厘米
15
1 from types import MethodType #从types模块中导入MethodType方法
2 class Student: #定义学生类
3 pass
4 def SetName(self,name): #定义SetName函数
5 self.name=name
6 def SetSno(self,sno): #定义SetSno函数
7 self.sno=sno
8 if __name__=='__main__':
9 stu1=Student() #定义Student类对象stu1
10 stu2=Student() #定义Student类对象stu2
11 stu1.SetName=MethodType(SetName,stu1) #为stu1对象绑定SetName方法
12 Student.SetSno=SetSno #为Student类绑定SetSno方法
13 stu1.SetName('李晓明')
14 stu1.SetSno('1810100')
15 #stu2.SetName('张刚')
16 stu2.SetSno('1810101')
绑定新方法示例:
动态扩展类与实例
示例03
代码缩进 4个空格
正文缩进1.27厘米
16
__slots__
PART 04
__slots__
【任务描述】
了解__slots__的用法
【关键步骤】
了解__slots__的用法
示例04
代码缩进 4个空格
正文缩进1.27厘米
18
1 class Person: #定义Person类
2 __slots__ = ('name') #定义允许动态扩展的属性
3 class Student(Person): #以Person类作为父类定义子类Student类
4 __slots__ = ('sno') #定义允许动态扩展的属性
5 class Postgraduate(Student): #以Student类作为父类定义子类Postgraduate类
6 pass
7 if __name__=='__main__':
8 stu=Student() #定义Student类对象stu
9 stu.sno='1810100' #为stu对象动态扩展属性sno
10 stu.name='李晓明' #为stu对象动态扩展属性name
11 #stu.tutor='马红' #取消前面的注释符则会报错
12 pg=Postgraduate() #定义Postgraduate类对象pg
13 pg.sno='1810101' #为pg对象动态扩展属性sno
14 pg.name='张刚' #为pg对象动态扩展属性name
15 pg.tutor='马红' #为pg对象动态扩展属性tutor
__slots__变量以限制可动态扩展的属性。例如,如果我们希望Student类中只能动态扩展sno和name属性:
__slots__
示例04
代码缩进 4个空格
正文缩进1.27厘米
19
@property
PART 05
@property装饰器,可以将类中属性的访问和赋值操作自动转为方法调用,这样可以在方法中对属性值的取值范围做一些条件限定。
@property
代码缩进 4个空格
正文缩进1.27厘米
21
@property
【任务描述】
了解@property的用法
【关键步骤】
了解@property的用法
示例05
代码缩进 4个空格
正文缩进1.27厘米
22
1 import datetime
2 class Student: #定义Student类
3 @property
4 def score(self): #用@property装饰器定义一个用于获取score值的方法
5 return self._score
6 @score.setter
7 def score(self, score): #用score.setter定义一个用于设置score值的方法
8 if score<0 or score>100: #不符合0~100的限定条件
9 print('成绩必须在0~100之间!')
10 else:
11 self._score=score
12 @property
13 def age(self): #用@property装饰器定义一个用于获取age值的方法
14 return datetime.datetime.now().year-self.birthyear
15 if __name__=='__main__':
16 stu=Student() #创建Student类对象stu
17 stu.score=80 #将stu对象的score属性赋值为80
18 stu.birthyear=2000 #将stu对象的birthyear属性赋值为2000
19 print('年龄:%d,成绩:%d'%(stu.age,stu.score))
20 #stu.age=19 #取消前面的注释符则会报错
21 stu.score=105 #将stu对象的score属性赋值为105
22 print('年龄:%d,成绩:%d'%(stu.age,stu.score))
@property
示例05
代码缩进 4个空格
正文缩进1.27厘米
23
年龄:18,成绩:80
成绩必须在0~100之间!
年龄:18,成绩:80
程序执行完毕后,将在屏幕上输出如下结果:
@property
示例05
代码缩进 4个空格
正文缩进1.27厘米
24
元类(metaclass)
PART 06
元类(metaclass)
【任务描述】
了解元类(metaclass)的用法
【关键步骤】
了解元类(metaclass)的用法
示例06
代码缩进 4个空格
正文缩进1.27厘米
26
1 class Student: #定义Student类
2 pass
3 stu=Student() #定义Student类的对象stu
4 print('stu所属的类是:',stu.__class__) #使用__class__属性获取所属的类
5 print('Student所属的类是:',Student.__class__)
元类(metaclass)可以看成是创建类时所使用的模板,也可以理解为是用于创建类的类。
stu所属的类是: <class '__main__.Student'>
Student所属的类是: <class 'type'>
程序执行完毕后,将在屏幕上输出如下结果:
元类(metaclass)
示例06
代码缩进 4个空格
正文缩进1.27厘米
27
单例模式
PART 07
单例模式(Singleton Pattern)是指在程序运行时确保某个类最多只有一个实例对象。
例如A类采用单例模式,则当创建A类对象时需要判断是否已存在A类对象:
如果不存在,则创建一个A类对象;
如果已存在,则直接使用已有的A类对象。
当一个类对象用于存储程序中多个线程的共享信息时,就要求该类对象只有一份,以避免空间浪费以及信息不一致等问题。
单例模式
29
在介绍单例模式的具体实现方式前,先介绍使用类创建对象时的执行过程:
当使用类创建一个对象时,使用__init__构造方法能够对对象中的属性做初始化操作。
实际上,在执行__init__前,还会执行类的一个内置的类方法__new__,其作用是创建对象并返回;
而之后执行的__init__则负责对__new__返回的对象做初始化。
单例模式
30
对象名(实参表) #等价于对象名.__call__(实参表)
__call__是类的另一个内置方法,通过在一个类中定义__call__方法,可以支持如下写法:
如果在元类中定义__call__方法,则可以改变类的实例化过程。
单例模式有多种实现方式,这里介绍一种通过重写元类的__init__和__call__实现单例模式的方法。
单例模式
代码缩进 4个空格
正文缩进1.27厘米
31
单例模式
【任务描述】
了解单例模式的用法
【关键步骤】
了解单例模式的用法
示例07
代码缩进 4个空格
正文缩进1.27厘米
32
1 class MyMetaclass(type): #以type作为父类创建子类MyMetaclass
2 def __new__(cls, *args, **kwargs):
3 print('MyMetaclass中的__new__被执行!')
4 return super().__new__(cls, *args, **kwargs)
5 def __init__(self, *args, **kwargs): #定义__init__方法
6 print('MyMetaclass中的__init__被执行!')
7 self._instance=None
8 return super().__init__(*args, **kwargs)
9 def __call__(self, *args, **kwargs):
10 print('MyMetaclass中的__call__被执行!')
11 if self._instance is None:
12 self._instance = super().__call__(*args, **kwargs)
13 return self._instance
14 class WebData(metaclass=MyMetaclass): #MyMetaclass作元类定义WebData类
15 def __new__(cls, *args, **kwargs):
16 print('WebData类中的__new__被执行!')
17 return super().__new__(cls)
18 def __init__(self):
19 print('WebData类中的__init__被执行!')
20 def __call__(self, timeout):
21 print('WebData类中的__call__被执行!')
22 self.timeout=timeout
单例模式
示例07
代码缩进 4个空格
正文缩进1.27厘米
33
23 if __name__=='__main__':
24 wb1=WebData() #定义WebData类对象wb1
25 wb2=WebData() #定义WebData类对象wb2
26 wb1(60) #等价于wb1.__call__(60)
27 print(wb1.timeout)
28 wb1.timeout=30
29 print(wb1.timeout)
30 wb2.timeout=15
31 print(wb1.timeout, wb2.timeout)
单例模式
MyMetaclass中的__new__被执行!
MyMetaclass中的__init__被执行!
MyMetaclass中的__call__被执行!
WebData类中的__new__被执行!
WebData类中的__init__被执行!
MyMetaclass中的__call__被执行!
WebData类中的__call__被执行!
60
30
15 15
程序执行完毕后,将在屏幕上输出如下结果:
示例07
代码缩进 4个空格
正文缩进1.27厘米
34
鸭子类型
PART 08
鸭子类型(Duck Typing)的含义是“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子”。
在鸭子类型中,关注的不是对象所属的类,而是一个对象能够如何使用。
在Python中编写一个函数,传递实参前其参数的类型并不确定,在函数中使用形参进行操作时只要传入的对象能够支持该操作程序就能正常执行。
鸭子类型
代码缩进 4个空格
正文缩进1.27厘米
36
鸭子类型
【任务描述】
了解鸭子类型的用法
【关键步骤】
了解鸭子类型的用法
示例08
代码缩进 4个空格
正文缩进1.27厘米
37
1 class Person: #定义Person类
2 def CaptureImage(self): #定义CaptureImage方法
3 print('Person类中的CaptureImage方法被调用!')
4 class Camera: #定义Camera类
5 def CaptureImage(self): #定义CaptureImage方法
6 print('Camera类中的CaptureImage方法被调用!')
7 def CaptureImageTest(arg): #定义CaptureImageTest方法
8 arg.CaptureImage() #通过arg调用CaptureImage方法
9 if __name__=='__main__':
10 p=Person() #定义Person类对象p
11 c=Camera() #定义Camera类对象c
12 CaptureImageTest(p)
13 CaptureImageTest(c)
鸭子类型示例:
鸭子类型
示例08
代码缩进 4个空格
正文缩进1.27厘米
38
Person类中的CaptureImage方法被调用!
Camera类中的CaptureImage方法被调用!
程序执行完毕后,将在屏幕上输出如下结果:
鸭子类型
示例08
代码缩进 4个空格
正文缩进1.27厘米
39
数据封装、继承和多态只是面向对象程序设计中最基础的3个概念。在Python中,面向对象还有很多高级特性。
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改。Python内置的@property装饰器可以把一个方法变成属性调用。
getter、setter、deleter-实验手册
任务背景
任务01
getter、setter、deleter-实验手册
定义一个Person类,包含属性first_name。定义该属性的setter、getter与deleter方法,使得通过Person类实例访问first_name属性时,能够自动调用相应方法。
任务需求
任务分析
建立Python项目
定义一个Person类,包含属性first_name
定义该属性的setter、getter与deleter方法
访问实例first_name属性时,自动调用相应方法
任务01
借用Pycharm工具,建立Python3项目
定义一个Person类,包含属性first_name
定义该属性的setter、getter与deleter方法
定义一个实例,在访问实例first_name属性时,自动调用相应方法
getter、setter、deleter-实验手册
任务步骤
任务结果
调用实例时,自动调用setter、getter与deleter方法
任务01
谢谢观看
$$