面向对象

 

  • 一、静态属性(就是数据属性)

首先,有一个需求:计算主人房间的面积

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9 r1 = Room('别墅','alex',30,20,10)
10 
11 r2 = Room('天桥下','wupeiqi',1,1,1)
12 
13 print('%s 住的 %s 面积是 %s平方米'%(r1.owner,r1.room_name,int(r1.width)*int(r1.high)))      ##  alex 住的 别墅 面积是 200平方米
14 print('%s 住的 %s 面积是 %s平方米'%(r2.owner,r2.room_name,int(r2.width)*int(r2.high)))      ##  wupeiqi 住的 天桥下 面积是 1平方米
room

但是这样子代码可读性比较差,我们可以把计算面积这个方法定义成一个函数,放在类里面

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     def room_area(self):
10         return '%s 住的 %s 面积是 %s平方米'%(self.owner,self.room_name,int(self.width)*int(self.high))
11 
12 ##  下面就直接调用类里面的 room_area() 方法就可以了
13 r1 = Room('别墅','alex',30,20,10)
14 r2 = Room('天桥下','wupeiqi',1,1,1)
15 
16 print(r1.room_area())       ##  alex 住的 别墅 面积是 200平方米
17 print(r2.room_area())       ##  wupeiqi 住的 天桥下 面积是 1平方米
room升级

我们再进行改动

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     @property       ##  把room_area()这个方法封装起来,让调用者调用的时候感受不到调用的过程
10     def room_area(self):
11         return '%s 住的 %s 面积是 %s平方米'%(self.owner,self.room_name,int(self.width)*int(self.high))
12 
13 r1 = Room('别墅','alex',30,20,10)
14 r2 = Room('天桥下','wupeiqi',1,1,1)
15 
16 print(r1.room_area)       ##  alex 住的 别墅 面积是 200平方米     ##  注意,这里是不是感觉到像是在调用一个类的数据属性一样,实际上这就是一个静态属性
17 print(r2.room_area)       ##  wupeiqi 住的 天桥下 面积是 1平方米
静态属性

这里面的 p1.room_area 就是静态属性,就是数据属性,而 @property 就把room_area() 这个函数封装起来,让调用者感觉不到调用的过程。

property补充:

一个静态属性property本质就是实现了get,set,delete三种方法

 1 class Test:
 2     @property
 3     def AAA(self):
 4         print('----------->property')
 5 
 6     @AAA.setter
 7     def AAA(self,value):
 8         print('------------>setter',value)
 9 
10     @AAA.deleter
11     def AAA(self):
12         print('------------>delter')
13 
14 t1 = Test()
15 t1.AAA      ##  ----------->property
16 t1.AAA = 1      ##  ------------>setter 1
17 del t1.AAA      ##  ------------>delter
property中的get、set、delete实现
 1 class Test:
 2     def AAA__get(self):
 3         print('----------->property')
 4 
 5     def AAA__set(self,value):
 6         print('------------>setter',value)
 7 
 8     def AAA__delete(self):
 9         print('------------>delter')
10     AAA = property(AAA__get,AAA__set,AAA__delete)
11 
12 t1 = Test()
13 t1.AAA      ##  ----------->property
14 t1.AAA = 1      ##  ------------>setter 1
15 del t1.AAA      ##  ------------>delter
另一种实现方式

 

应用:

 1 class Good:
 2     def __init__(self):
 3         self.price = 100
 4         self.discount = 0.8
 5 
 6     def get_price(self):
 7         return self.price*self.discount
 8 
 9     def set_price(self,value):
10         self.price = value
11 
12     def del_price(self):
13         del self.price
14 
15     good_price = property(get_price,set_price,del_price)
16 
17 good1 = Good()
18 print(good1.good_price)     ##  80.0
19 good1.good_price = 10
20 print(good1.good_price)     ##  8.0
21 del good1.good_price
用property实现商品价格的操作

 

 

  • 二、类方法

假设有一个需求:不创建一个对象,直接用类调用类的函数属性

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     def func(self):
10         print('——————》类直接调用')
11 
12 Room.func()
13 # 运行结果:Traceback (most recent call last):
14 #   File "F:/PycharmProjects/复习/面向对象/面向对象思想.py", line 333, in <module>
15 #     Room.func()
16 # TypeError: func() missing 1 required positional argument: 'self'
类直接调用

这里报错的原因很容易看出来,func()缺少一个参数,而我们要运行这个函数就必须实例化一个对象,这又与我们的需求想违背。这个时候就衍生出了类方法出来

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     @classmethod
10     def func(cls):      ##  这里的cls 就是类自己,相当于把类本身作为参数传进去了
11         print(cls)
12         print('——————》类直接调用')
13 
14 Room.func()
15 # <class '__main__.Room'>
16 # ——————》类直接调用
类方法

 只要加了 @classmethod 就是一个类方法,这里注意一点:对象同样也可以调用到这个类方法

 

  • 三、静态方法

下面我们想在类里创建一个函数,既不和类绑定,有不和对象绑定

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     def test():
10         print('这是test函数')
11 
12 Room.test()     ##  这是test函数
13 
14 r1 = Room('别墅','alex',30,20,10)
15 r1.test()
16 #  运行结果: Traceback (most recent call last):
17 #   File "F:/PycharmProjects/复习/面向对象/面向对象思想.py", line 372, in <module>
18 #     r1.test()
19 # TypeError: test() takes 0 positional arguments but 1 was given

但是这里报错了,原因是test()这个函数还是一个类的函数,而对象调用这个函数会自动将对象传进去,由于test)这个函数没有接收参数,所以就报错了

所以这里就引出了静态方法

 1 class Room:
 2     def __init__(self,name,owner,length,width,high):
 3         self.room_name = name
 4         self.owner = owner
 5         self.length = length
 6         self.width = width
 7         self.high = high
 8 
 9     @staticmethod       ##  这里staticmethod就是一个静态方法,这个函数不跟类和对象联系,就是一个普通的函数
10     def test():
11         print('这是test函数')
12 
13 r1 = Room('别墅','alex',30,20,10)
14 r1.test()       ##  这是test函数
静态方法

这时test()就是一个普通的函数了,类和对象都可以直接调用了。静态方法是类里面的一个工具包

 

  • 小结:

静态属性既可以访问对象属性,也可以访问类属性

类方法只能访问类的属性,不能访问对象属性

静态方法既不能访问对象属性,也不能访问类的属性

 

  • 四、组合

顾名思义,组合就是用多个类组合起来,类与类之间存在联系

什么时候用到组合呢?

当类与类之间没有共同点的时候,但是类与类之间存在联系的时候,这个时候就可以用到组合了

我们先来看一个简单的组合

我们都知道人有手(hand),脚(foot),头(head),躯干(trunk),这些部分都和人体有联系,但是又互相没有共同点,那我们用组合来表示。

 1 class Hand:
 2     pass
 3 class Foot:
 4     pass
 5 class Head:
 6     pass
 7 class Trunk:
 8     pass
 9 class People:
10     def __init__(self,name,age):
11         self.name = name
12         self.age = age
13         self.hand = Hand()      ##  这里实际就是多个类组合起来了
14         self.foot = Foot()
15         self.head = Head()
16         self.trunk = Trunk()
17         
18 p1 = People('alex','18')
简单的组合

下面我们来用组合来完成一个学校选课系统:

需求:

    有不同的校区(School),课程(Course),老师(Teacher)。课程和校区关联,老师和课程关联

 1 class Teacher:
 2     def __init__(self,teacher_name,teacher_age,teacher_gender):
 3         self.teacher_name = teacher_name
 4         self.teacher_age = teacher_age
 5         self.teacher_gender = teacher_gender
 6 
 7 class Course:
 8     def __init__(self,course_name,course_price,course_period,teacher):
 9         self.course_name = course_name
10         self.course_price = course_price
11         self.course_period = course_period
12         self.teacher = teacher
13 
14 class School:
15     def __init__(self,school_name,school_addr,course,teacher):
16         self.school_name = school_name
17         self.school_addr = school_addr
18         self.teacher = teacher
19         self.course = course
20 
21 school_list = {
22     '1':'老男孩',
23     '2':'北大青鸟',
24     '3':'泽林'
25 }
26 addr_list = {
27     '1':'北京',
28     '2':'深圳',
29     '3':'上海'
30 }
31 course_list = {
32     '1':'python',
33     '2':'c++',
34     '3':'java'
35 }
36 while True:
37     print('学校有:%s'%school_list)
38     school_choice = input('请输入学校编号:')
39     print('校区有%s'%addr_list)
40     addr_choice = input('请选择校区地址:')
41     print('学校课程有%s'%course_list)
42     course_choice = input('请选择学校课程编号:')
43     school_name = school_list[school_choice]
44     school_addr = addr_list[addr_choice]
45     course_name = course_list[course_choice]
46     course_price = input('请输入课程价格:')
47     course_period = input('请输入课程周期:')
48     teacher_name = input('请输入老师名字:')
49     teacher_age = input('请输入老师年龄:')
50     teacher_gender = input('请输入老师性别:')
51 
52     teacher = Teacher(teacher_name, teacher_age, teacher_gender)
53     course = Course(course_name,course_price,course_period,teacher)
54     school = School(school_name, school_addr, course, teacher)
55 
56     print('恭喜您选课成功!下面是您的课程信息:')
57     print('校区名字:%s'%school.school_name)
58     print('校区地址:%s'%school.school_addr)
59     print('选择的课程:%s'%school.course.course_name)
60     print('教授的老师:%s'%school.teacher.teacher_name)
61     
62     ##  恭喜您选课成功!下面是您的课程信息:
63     # 校区名字:老男孩
64     # 校区地址:深圳
65     # 选择的课程:python
选课系统

 

 

  • 五、面向对象的三大特性

面向对象三大特性:继承  多态  封装

  • (一)继承

 1、继承分为两种:单继承和多继承

 1 ##  继承
 2 ##  单继承
 3 class Dad:
 4     pass
 5 class Son(Dad):
 6     pass
 7 
 8 ##  多继承
 9 class GrandDad:
10     pass
11 class Dad:
12     pass
13 class Son(GrandDad,Dad):
14     pass
单继承和多继承

 

2、继承了哪些属性?

继承父类会将父类的所有属性继承过来。首先子类会在自己的属性里面去找,如果没有才会去父类找属性

 1 class Dad:
 2     money = 10
 3     def __init__(self,name):
 4         self.name = name
 5     def hit_son(self):
 6         print('%s正在打儿子'%self.name)
 7 
 8 class Son(Dad):
 9     money = 10000
10 
11 son1 = Son('alex')
12 print(son1.money)       #   10000
13 son1.hit_son()          #   alex正在打儿子
继承的属性

 

3、什么时候用继承?

(1)、当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如胳膊、腿、身体类等
(2)、当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
例如:
猫可以:喵喵叫、吃喝拉撒
狗可以:汪汪叫、吃喝拉撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫和狗实现他们所有的功能

我们可以发现狗和猫都有共同点,那我们就可以写一个动物类,然后再让猫和狗分别继承动物类

 

4、继承同时具有两种含义
含义一:继承基类的方法,并且做出自己的改变或扩展(代码重用)
含义二:声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法

 1 import abc
 2 class   All_file(metaclass=abc.ABCMeta):    ##  定义一个接口
 3     @abc.abstractmethod     ##  只要继承了这个接口,必须要定义一个函数来描述这个信息
 4     def read(self):
 5         pass
 6     @abc.abstractmethod
 7     def write(self):
 8         pass
 9     
10 class   Disk(All_file):
11     def read(self):         ##  必须要有一个详细的函数来描述信息
12         print('disk read')
13     def write(self):
14         print('disk write')
15         
16 class   Cd(All_file):
17     def read(self):
18         print('CD read')
19     def write(self):
20         print('CD write')
21         
22 class   Mem(All_file):
23     def read(self):
24         print('mem read')
25     def write(self):
26         print('mem write')
27         
28 disk = Disk()
29 disk.write()
接口继承

实践中,继承的第一种含义意义不大,甚至常常都是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要,它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”--- 这在程序设计上,叫归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好像Linux的范文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘还是屏幕。

接口继承的原则是,基类定义了一些方法,而基类不具体实现这些方法,但是规定了继承这个基类的子类必须要具体实现这些方法。

 

5、继承的顺序

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)

如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

 1 #  新式类:基类有继承关系,pytho3都是
 2 #  经典类:基类没有继承关系
 3 class A:
 4     def test(self):
 5         print('A')
 6     # pass
 7 class B(A):
 8     def test(self):
 9         print('B')
10     # pass
11     # pass
12 class C(A):
13     def test(self):
14         print('C')
15     # pass
16 class D(B):
17     def test(self):
18         print('D')
19     # pass
20 class E(C):
21     def test(self):
22         print('E')
23     # pass
24 class F(D,E):
25     def test(self):
26         print('F')
27     # pass
28 f = F()     ##  继承顺序:F-->D-->B-->E-->C-->A
29 f.test()
30 print(F.__mro__)  ##  只有新式类才有这个方法      ##  继承顺序:(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
31 #新式类继承顺序:F->D->B->E->C->A
32 #经典类继承顺序:F->D->B->A->E->C
33 #python3中统一都是新式类
34 #pyhon2中才分新式类与经典类
继承顺序

 

  • (二)多态

什么是多态:

(以封装和继承为前提)不同的子类调用相同的方法,执行的过程(逻辑)是不同的,最后产生了不同的结果

注意,要产生了不同的对象并且调用了相同的方法才叫多态,这个类就是一个多态。多态反映的是多个对象调用同一个父类的同一个方法,实现的过程以及最后产生的结果是不一样的。Python本身就是一个多态

 1 class H20:
 2     def __init__(self,statu,temp):
 3         self.statu = statu
 4         self.temp = temp
 5     def water_turn(self):
 6         if self.temp <= 0:
 7             print('水温度太低变成了%s'%self.statu)
 8         elif self.temp < 100:
 9             print('水还是%s'%self.statu)
10         else:
11             print('水温度太高蒸发变成了%s'%self.statu)
12 class Water(H20):
13     pass
14 class Ice(H20):
15     pass
16 class Steam(H20):
17     pass
18 ice = Water('ice',-20)      ##  不同对象产生了并且调用了相同的方法,那就体现了多态的思想了
19 water = Ice('water',25)
20 steam = Steam('steam',120)
21 def turn(statu):
22     statu.water_turn()
23 turn(ice)   ##  水温度太低变成了ice
24 turn(water)     ##  水还是water
25 turn(steam)     ##  水温度太高蒸发变成了steam
26 
27 ##  不同的对象调用同一个父类的同一个方法,最后的结果是不一样的
多态
  • 总结:

类的继承有两层意义:1、改变 2、扩展
多态就是类的这两层意义的一个具体的实现机制
即,调用不同的类实例化得到对象下的相同的方法,实现的过程不一样
Python中的标准类型就是多态概念的一个很好的示范

 

  • (三)封装(内部能调用,外部不能调用)

封装分为两部分:封和装,顾名思义,装就是把一些数据属性和函数属性装到属性字典里,封就是把类里面的数据属性还有函数属性封起来,只提供类名这个接口给调用者调用,调用者感受不到具体的实现逻辑。

封装是面向对象的一种思想

(1)第一层面的封装

这就是一个麻袋,这本身就是一个封装

(2)第二层面的封装

类中定义私有的,只能在内部中使用,外部不能使用

 1 __id = 'linhaifeng'
 2 class Package:
 3     _star = '地球'        ##  Python约定了封装用这个格式,但是外部还是能够访问到的
 4     __star = 'earth'        ##  Python会把__star 改名为'_Package__star',还是能够访问到的
 5     def __init__(self,name,money,id):
 6         self.name = name
 7         self.money = money
 8         self.id = id
 9 
10 p = Package('alex',10000000,__id)
11 print(p._star)      ##  地球      虽然能访问得到_star 这个数据属性,但是Python约定了只要是 _ 开头的,外部就不应该调用这个数据属性
12 # print(p.__star)     ##  AttributeError: 'Package' object has no attribute '__star'
13 print(p._Package__star)       ##   earth  用这一个格式还是能够访问到,但是不应该调用
单下划线以及双下划线的封装

Python约定了,只要类里面的 数据属性用了下划线(_)开头,或者双下划线(__)开头,那么这个属性就只能内容用,外部不能直接调用,当然外部想调用还是可以的,但是不应该这么做。

(3)第三层面的封装

第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正意义上的封装)

 1 __id = 'linhaifeng'
 2 class Package:
 3     _star = '地球'        ##  Python约定了封装用这个格式,但是外部还是能够访问到的
 4     __star = 'earth'        ##  Python会把__star 改名为'_Package__star',还是能够访问到的
 5     def __init__(self,name,money,id):
 6         self.name = name
 7         self.money = money
 8         self.id = id
 9     def get_id(self):       ##  访问函数
10         return self.__star      ##  提供一个接口外部可以访问到封装的数据
11 p = Package('alex',10000000,__id)
12 
13 print(p.get_id())       ##  外部可以调用这个接口
第三层面的封装(提供一个接口给外部使用)

 

  • 六、反射(自省)

(1)什么是反射?
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

(2)四个可以实现自省的函数(系统自带的)
下列方法适用于类和对象

 1 # hasattr(object,name)
 2 # 判断对象里有没有name这个属性
 3 class People:
 4     def __init__(self,name,age):
 5         self.name = name
 6         self.age = age
 7     def run(self):
 8         print('%s开始跑起来了'%self.name)
 9 
10 p1 = People('alex',18)
11 print(p1.__dict__)  ##  {'name': 'alex', 'age': 18}
12 print(hasattr(p1,'name'))       ##  True    ##    这里实质上就是在操作p1的字典属性
hasattr(object,name)
 1 # getattr(object,name,default=None)
 2 ##  查找对象的属性,如果没有则默认报错,default可以修改
 3 class People:
 4     def __init__(self,name,age):
 5         self.name = name
 6         self.age = age
 7     def run(self):
 8         print('%s开始跑起来了'%self.name)
 9 
10 p1 = People('alex',18)
11 print(p1.name)      ##  alex
12 print(getattr(p1,'name'))       ##  alex
13 print(getattr(p1,'gender','查不到就报这条信息'))     ##  查不到就报这条信息
getattr(object,name,default=None)
 1 # setattr(object,name,value)
 2 class People:
 3     def __init__(self,name,age):
 4         self.name = name
 5         self.age = age
 6     def run(self):
 7         print('%s开始跑起来了'%self.name)
 8 
 9 p1 = People('alex',18)
10 # p1.gender = '男'
11 setattr(p1,'gender','')        ##  等同于上面
12 print(p1.gender)      ##    男
setattr(object,name,value)
 1 # delattr(object,name)
 2 class People:
 3     def __init__(self,name,age):
 4         self.name = name
 5         self.age = age
 6     def run(self):
 7         print('%s开始跑起来了'%self.name)
 8 
 9 p1 = People('alex',18)
10 # del p1.name
11 # print(p1.__dict__)      ##  {'age': 18}
12 delattr(p1,'name')      ##  等同于上面操作
13 print(p1.__dict__)      ##  {'age': 18}
delattr(object,name)

四个方法都是对实例的字典属性进行操作,注意里面的参数name应该传入一个字符串形式。

 

(3)什么时候用反射?

为什么用反射呢?例子:有两个程序员A和B,两个程序员在完成一个项目,B在写程序的时候需要用到A的类,

但是A去跟女朋友度蜜月了,还没完成他写的类,B想到了反射,用反射机制B利益完成自己的代码,等A回来后再继续完成类的定义并且去实现B想要的功能

1 class A:
2     def run(self):
3         print('A的程序开始运行')
A程序员的代码
1 f1 = A()
2 if hasattr(f1,'run'):
3     f1.run()
4 else:
5     print('A的代码还没写好,执行下面的程序')
B程序员的代码

总之反射的好处就是,可以先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实就是一种“后期绑定”,什么意识?即你可以事先把主要的逻辑写好(只定义接口),后期再去实现接口的功能。

补充:动态导入模块

1 #############################       模块导入补充:
2 # import 多态      ##  第一种形式
3 # m = __import__('多态')         ##   第二种形式,导入的模块名是字符串形式,注意这个时候就不能用第一种形式了,注意导入的是顶级文件
4 # import importlib
5 # m = importlib.import_module('多态')       ##  第三种形式,导入的是底级文件
动态导入模块

 

 

(4)类的三个内置 __attr__属性

1 class Foo:
2     def __init__(self,x):
3         self.x = x
4     def __getattr__(self, item):      ##  item就是传入不存在的值
5         print('执行__getattr__%s'%item)
6 f = Foo(5)
7 f.y    ##   执行__getattr__y      ##  没有这个方法或者属性的时候会调用__getattr__方法
__getattr__()
1 class Foo:
2     def __init__(self,x):
3         self.x = x
4     def __delattr__(self, item):
5         print('执行__delattr__')
6 f = Foo(5)
7 del f.x     ##  执行__delattr__  ##  删除操作会调用__delattr__方法,删除的本质就是在操作底层的属性字典
8 print(f.__dict__)
__delattr__()
1 class Foo:
2     def __init__(self,x):
3         self.x = x
4     def __setattr__(self, key, value):      ##  操作属性字典
5         print('执行__setattr__')      ##  如果只执行这个打印操作,属性字典就不会加入值
6         # self.key = value    ##  执行会触发递归,因为self.key = value就是在触发 __setattr__
7         # self.__dict__[key] = value      ##  直接加入类的属性字典中
8 f = Foo(5)      #   执行__setattr__
9 print(f.__dict__)
__setattr__()

 

 

(5)继承的方式来完成包装

每个数据类型都有自己的函数,但是有时候这些内置函数不能满足我们的需求,那我们就必须要重写这些内置函数

我们下面有需求:

需求:修改list中的append操作,只能添加字符串类型
 1 class List(list):
 2     def append(self,name):
 3         if type(name) is str:
 4             super().append(name)
 5         else:
 6             print('请输入一个字符串类型')
 7 
 8 l = List()
 9 l.append(123)       ##  请输入一个字符串类型
10 print(l)        ##  []
修改list的append函数

 

(6)组合的方式完成授权

授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能。其它的则保持原样。

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

 1 import time
 2 class Open:
 3     def __init__(self,filename,way='r+',encoding = 'utf-8'):
 4         self.file = open(filename, way, encoding=encoding)      ##  用组合的方法把类的功能全部调用过来
 5         self.filename = filename
 6         self.way = way
 7         self.encourding = encoding
 8     def write(self,line):
 9         t = time.strftime('%Y-%m-%d %X')
10         self.file.write('%s %s\n'%(t,line))
11         
12     def __getattr__(self, item):
13         return getattr(self.file,item)      ##  没有找到则把open这个类里面的方法返回
14     
15 f = Open('a.txt','w+')
16 f.write('123456789')
17 f.write('cpu超载负荷')
18 f.write('U盘修复')
19 f.seek(0)
20 a = f.read()
21 print(a)
组合的方式完成授权

 

  • 七、isinstance和issubclass

isinstance(obj,cls) 判断obj是否是类cls的实例化

1 class Foo:
2     pass
3 obj = Foo()
4 print(isinstance(obj,Foo))      ##  True
isinstance(obj,cls)

issubclass(Cls,Foo) 判断类Cls是否继承了 类Foo

1 class Foo:
2     pass
3 class Cls(Foo):
4     pass
5 print(issubclass(Cls,Foo))      ##  True
issubclass(Cls,Foo)

 

  • 八、getattribute

先来看一下getattribute 的用法:

 1 class Foo:
 2     def __init__(self,x):
 3         self.x = x
 4     def __getattr__(self, item):      ##  item就是传入不存在的值
 5         print('执行__getattr__%s'%item)
 6     def __getattribute__(self, item):   ##  无论查找的值有还是没有都会触发getattribute,可以加入异常抛出让getattr触发
 7         print('执行的是getattribute')
 8         raise AttributeError('错误啦')
 9 f = Foo(5)      ##  执行的是getattribute
10 f.y   ##    执行的是getattribute      ##  没有这个方法或者属性的时候会调用__getattr__方法
getattribute

简单来说,__getattribute__()就是就是__getattr__()的大哥,无论赋的值是否存在,都会触发getattribute,当不存在时,会先触发getattribute,getattribute在抛出一个

AttributeError异常给getattr,执行getattr语句。

 

  • 九、__item__系列方法
 1 class   Foo:
 2     def __getitem__(self, item):
 3         print('---->getitem')
 4         return self.__dict__[item]
 5 
 6     def __setitem__(self, key, value):
 7         print('--->setitem')
 8         self.__dict__[key] = value
 9 
10     def __delitem__(self, key):
11         print('--->delitem')
12         self.__dict__.pop(key)
13 
14 f = Foo()
15 f['name'] = 'alex'      ##  --->setitem    ##  注意:要用字典的形式来调用setitem
16 f['age'] = 18           ##  --->setitem
17 print(f.__dict__)       ##  {'name': 'alex', 'age': 18}
18 print(f['name'])        ##  ---->getitem        alex
19 del f['name']           ##  --->delitem
20 print(f.__dict__)       ##  {'age': 18}
__getitem__(self, item)、__setitem__(self, key, value)和__delitem__(self, key)

总结:

__item__和__attr__的区别:

item方法和attr方法的效果是一样的,只不过调用方式不一样,item要用f[]这种形式来调用,而attr要用f.name这种形式来调用。

 

  • 十、__str__ 和 __repr__、自定制__format__方法
1 class Foo:
2     def __init__(self,name,age):
3         self.name = name
4         self.age = age
5     def __str__(self):
6         return '名字是%s,年龄是%s'%(self.name,self.age)       ##  自定制str
7 f = Foo('agen',18)
8 print(f)    ##  名字是agen,年龄是18    ##  print实质上就是 print(str(f)) 或者 print(f.__str__())
__str__()
 1 class Foo:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5     def __str__(self):
 6         return '这是str'      ##  自定制str
 7     def __repr__(self):
 8         return '名字是%s,年龄是%s'%(self.name,self.age)       ## 这个作用的是解释器
 9 f = Foo('agen',18)
10 print(f)        ##  这是str
11     ##  print找的顺序是:str(f)——》f.__str__()---->repr(f)
__repr__()

__str__ 和 __repr__都是用来控制屏幕输出的,控制实例化出来的例子是一个什么类型的(显示方式)。

print找的顺序是:str(f)——》f.__str__()---->repr(f)

 1 ##  format的用法:
 2 # s = '{0}{0}{0}'.format('%')
 3 # print(s)
 4 
 5 ##  自定制format方法:
 6 formate_dic = {
 7     'ymd':'{0.year}{0.mon}{0.day}',
 8     'm-d-y':'{0.mon}-{0.day}-{0.year}',
 9     'y;m;d':'{0.year}:{0.mon}:{0.day}'
10 }
11 class Foo:
12     def __init__(self,year,mon,day):
13         self.year = year
14         self.mon = mon
15         self.day = day
16     def __format__(self, format_spec):
17         if not format_spec or format_spec not in formate_dic:
18             format_spec = 'ymd'
19         fm = formate_dic[format_spec]
20         return fm.format(self)
21 day1 = Foo(2019,5,9)
22 print(format(day1,'m-d-y'))
自定制__format__方法

 

  • 十一、__slots__ 方法

1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

2、什么时候使用 __slots__ 呢?

当你的类里面的属性很少时,而这个类产生的对象有很多的时候,那么这个时候就会用到 __slots__ 来代替类中的 __dict__属性字典,这样可以减少内存占用

当然__slots__ 也有缺点,就是产生的对象只能调用类中定义的 __slots__ 属性,而不能定义类中没有的属性

3、slots 的优点:可以节省内存,使用slots后创建的对象不再有自己的字典属性,而且不能添加slots里面没有的变量。

1 class Foo:
2     __slots__ = ['name','age']  ##  {'name':None,'age':None}
3 p1 = Foo()
4 p1.name = 'agen'
5 p1.age = 18
6 print(p1.name,p1.age)
7 #print(p1.__dict__)      ##  AttributeError: 'Foo' object has no attribute '__dict__'    ##  使用__slots__之后实例就没有__dict__了
8 print(p1.__slots__)     ##  ['name', 'age']
__slots__

 

  • 十二、__call__()方法

对象加 ()就可以触发类中的 __call__ 方法

1 class Foo:
2     def __init__(self,name):
3         self.name = name
4     def __call__(self, *args, **kwargs):
5         print('这是call方法执行的-->')
6 f = Foo('alex')
7 f()     ##  这是call方法执行的-->    ##  对象加()触发类的__call__方法
__call__()

 

 

  • 十三、__next__和 __iter__ 实现迭代器协议
 1 class Foo:
 2     def __init__(self,n):
 3         self.n = n
 4     def __iter__(self):
 5         return self
 6     def __next__(self):
 7         if self.n > 5:
 8             raise StopIteration('迭代器结束了')
 9         self.n += 1
10         return self.n
11 f1 = Foo(1)
12 print(f1.__next__())
13 print(f1.__next__())
14 print(f1.__next__())
15 for i in f1:
16     print(i)
迭代器协议
 1 class Fib:
 2     def __init__(self):
 3         self.a = 1
 4         self.b = 1
 5     def __iter__(self):
 6         return self
 7     def __next__(self):
 8         self.a,self.b = self.b,self.a + self.b
 9         if self.a >100:
10             raise StopIteration('迭代器结束了')
11         return self.a
12 f1 = Fib()
13 print(f1.__next__())
14 for i in f1:
15     print(i)
迭代器协议实现斐波那契数列

 

 

  • 十四、描述符(__get__,__set__,__delete__)

1 描述符是什么:

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
2     def __get__(self, instance, owner):
3         pass
4     def __set__(self, instance, value):
5         pass
6     def __delete__(self, instance):
7         pass
描述符协议

2 描述符是干什么的:

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

 1 class Foo:
 2     def __get__(self, instance, owner):
 3         print('触发get')
 4     def __set__(self, instance, value):
 5         print('触发set')
 6     def __delete__(self, instance):
 7         print('触发delete')
 8 
 9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
10 f1=Foo()
11 f1.name='egon'
12 f1.name
13 del f1.name
14 #疑问:何时,何地,会触发这三个方法的执行
引子:描述符类产生的实例进行属性操作并不会触发三个方法的执行
 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str调用')
 5     def __set__(self, instance, value):
 6         print('Str设置...')
 7     def __delete__(self, instance):
 8         print('Str删除...')
 9 
10 #描述符Int
11 class Int:
12     def __get__(self, instance, owner):
13         print('Int调用')
14     def __set__(self, instance, value):
15         print('Int设置...')
16     def __delete__(self, instance):
17         print('Int删除...')
18 
19 class People:
20     name=Str()
21     age=Int()
22     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
23         self.name=name
24         self.age=age
25 
26 #何地?:定义成另外一个类的类属性
27 
28 #何时?:且看下列演示
29 
30 p1=People('alex',18)
31 
32 #描述符Str的使用
33 p1.name
34 p1.name='egon'
35 del p1.name
36 
37 #描述符Int的使用
38 p1.age
39 p1.age=18
40 del p1.age
41 
42 #我们来瞅瞅到底发生了什么
43 print(p1.__dict__)
44 print(People.__dict__)
45 
46 #补充
47 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
48 print(type(p1).__dict__ == People.__dict__)
描述符应用之何时?何地?

3 描述符分两种
一 数据描述符:至少实现了__get__()和__set__()

1 class Foo:
2     def __set__(self, instance, value):
3         print('set')
4     def __get__(self, instance, owner):
5         print('get')

二 非数据描述符:没有实现__set__()

1 class Foo:
2     def __get__(self, instance, owner):
3         print('get')

 4 注意事项:优先级顺序
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

 1 class Foo:
 2     def __get__(self):
 3         print('触发__get__方法')
 4     def __set__(self):
 5         print('触发__set__方法')
 6     def __delete(self):
 7         print('触发__delete__方法')
 8 
 9 class Test:
10     x = Foo()
11 
12 Test.x = 10     ##  类属性 > 数据描述符
13 print(Test.x)   ## 10
类属性 > 数据描述符
 1 class Foo:
 2     def __get__(self,instance, owner):
 3         print('触发__get__方法')
 4     def __set__(self,instance, value):
 5         print('触发__set__方法')
 6     def __delete__(self,instance):
 7         print('触发__delete__方法')
 8 
 9 class Test:
10     x = Foo()
11 
12 test = Test()
13 test.x          ##  触发__get__方法     ##  数据描述符 > 实例属性
14 test.x = 10     ##  触发__set__方法
15 del test.x      ##  触发__delete__方法
数据描述符 > 实例属性
 1 class Foo:
 2     def __get__(self,instance, owner):
 3         print('触发__get__方法')
 4 
 5 class Test:
 6     x = Foo()
 7     def __init__(self):
 8         self.x = 100
 9 
10 test = Test()
11 print(test.x)   ##  100     ##  实例属性 > 非数据描述符
12 test.x = 10
13 print(test.x)     ##    10
实例属性 > 非数据描述符
1 class Foo:
2     def func(self):
3         print('我胡汉三又回来了')
4 
5     def __getattr__(self, item):
6         print('找不到了当然是来找我啦',item)
7 f1=Foo()
8 
9 f1.xxxxxxxxxxx
非数据描述符>找不到

 

5 描述符使用

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

 1 class Miaoshu:
 2     def __get__(self, instance, owner):
 3         print('执行get')
 4         print(instance)
 5         print(owner)        ##  Foo这个类
 6     def __set__(self, instance, value): ##  这里的self 是类 Miaoshu 的对象
 7         print('执行set')
 8         print(instance)     ##  实例的地址值
 9         print(value)        ##  实例的值
10     def __delete__(self, instance):
11         print('执行delete')
12         print(instance)
13 
14 class Foo:
15     name = Miaoshu()        ##-------------->   此时name就是数据描述符的代理
16     def __init__(self,name,age,sex):
17         self.name = name
18         self.age = age
19         self.sex = sex
20 p1 = Foo('alex',18,'')
未给实例操作字典属性
 1 class Typed:
 2     def __init__(self,key):
 3         self.key = key
 4     def __get__(self,instance,owner):
 5         print('get方法')
 6         # print('instance-->',instance)
 7         # print('owner-->',owner)         ##  ##  owner--> <class '__main__.Foo'>     指的就是 Foo这个类
 8         return instance.__dict__[self.key]      ##  将实例里是属性字典的key返回
 9 
10     def __set__(self,instance,value):
11         print('set方法')
12         # print('instance-->',instance)       #    ##  instance--> <__main__.Foo object at 0x00000000021911D0>  指的就是实例
13         # print('value-->',value)         ##   value--> alex
14         instance.__dict__[self.key] = value     ##  真正实现了赋值操作
15 
16     def __delete__(self,instance):
17         print('delete方法')
18         instance.__dict__.pop(self.key)
19 
20 class Foo:
21     name = Typed('name')
22     age = Typed('age')
23     def __init__(self,name,age,gender):
24         self.name = name
25         self.age = age
26         self.gender = gender
27 
28 f1 = Foo('alex',18,'')
29 print(f1.name)    ##  alex
30 print(f1.__dict__)      ##  {'name': 'alex', 'age': 18, 'gender': '男'}
31 del f1.name
32 print(f1.__dict__)      ##  {'age': 18, 'gender': '男'}
操作了实例的属性字典
 1 class Typed:
 2     def __init__(self,key):
 3         self.key = key
 4     def __get__(self,instance,owner):
 5         print('get方法')
 6         return instance.__dict__[self.key]      ##  将实例里是属性字典的key返回
 7 
 8     def __set__(self,instance,value):
 9         print('set方法')
10         if type(value) is not str:      ##  判断传入的值是否是一个字符串
11             raise TypeError('你传入的不是一个数据类型,类型错误!')
12         instance.__dict__[self.key] = value     ##  真正实现了赋值操作
13 
14     def __delete__(self,instance):
15         print('delete方法')
16         instance.__dict__.pop(self.key)
17 
18 class Foo:
19     name = Typed('name')
20     age = Typed('age')
21     def __init__(self,name,age,gender):
22         self.name = name
23         self.age = age
24         self.gender = gender
25 
26 f1 = Foo('alex',18,'')
27 print(f1.__dict__)      ##  {'name': 'alex', 'age': 18, 'gender': '男'}
28 f1.name = 123       ##  TypeError: 你传入的不是一个数据类型,类型错误!
判断传入的值是否为字符串类型
 1 class Typed:
 2     def __init__(self,key,expect_type):
 3         self.key = key
 4         self.expect_type = expect_type
 5     def __get__(self,instance,owner):
 6         print('get方法')
 7         return instance.__dict__[self.key]      ##  将实例里是属性字典的key返回
 8 
 9     def __set__(self,instance,value):
10         print('set方法')
11         if type(value) is not self.expect_type:      ##  判断传入的值是否是一个字符串
12             raise TypeError('%s传入的不是一个%s!'%(self.key,self.expect_type))
13         instance.__dict__[self.key] = value     ##  真正实现了赋值操作
14 
15     def __delete__(self,instance):
16         print('delete方法')
17         instance.__dict__.pop(self.key)
18 
19 class Foo:
20     name = Typed('name',str)        ##  这里多加一个参数(判断类型)
21     age = Typed('age',int)
22     def __init__(self,name,age,gender):
23         self.name = name
24         self.age = age
25         self.gender = gender
26 
27 f1 = Foo('alex',18,'')
28 f2 = Foo('agon','12','')       ##  TypeError: age传入的不是一个<class 'int'>!
判断语句优化

 

 6、类的装饰器

 1 def func(obj):
 2     print('-------->',obj)      ##  --------> <class '__main__.Foo'>
 3     obj.x = 1
 4     obj.z = 2
 5     obj.y = 3
 6     return obj
 7 
 8 @func  ##  Foo = func(Foo)
 9 class Foo:
10     pass
11 print(Foo.__dict__)     ##  {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'z': 2, 'y': 3}
基本原理
 1 def Deco(**kwargs):
 2     def func(obj):
 3         print('------->',obj)
 4         for key,val in kwargs.items():
 5             setattr(obj,key,val)
 6         return obj
 7     return func
 8 
 9 @Deco(x=1,y=2,z=3)     ##   1、先运行 Deco(x=1,y=2,z=3) ---> func    2、再 @func
10 class Foo:
11     pass
12 print(Foo.__dict__)     ##  {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
13 
14 @Deco(name='alex')
15 class Test:
16     pass
17 print(Test.__dict__)
18 
19 ##{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None, 'name': 'alex'}
类的装饰器加强版

 终极版

 1 class Typed:
 2     def __init__(self,key,expect_type):
 3         self.key = key
 4         self.expect_type = expect_type
 5     def __get__(self,instance,owner):
 6         print('get方法')
 7         return instance.__dict__[self.key]      ##  将实例里是属性字典的key返回
 8 
 9     def __set__(self,instance,value):
10         print('set方法')
11         if type(value) is not self.expect_type:      ##  判断传入的值是否是一个字符串
12             raise TypeError('%s传入的不是一个%s!'%(self.key,self.expect_type))
13         instance.__dict__[self.key] = value     ##  真正实现了赋值操作
14 
15     def __delete__(self,instance):
16         print('delete方法')
17         instance.__dict__.pop(self.key)
18 
19 def Deco(**kwargs):    ##   {'name':str,'age':int}
20     def func(obj):
21         for key,val in kwargs.items():
22             setattr(obj,key,Typed(key,val))     ##  这里实际上就是给类Foo增加描述符
23         return obj
24     return func
25 
26 @Deco(name=str,age=int)     ##  `Deco(name=str,age=int) --->    @func(Foo)
27 class Foo:
28     def __init__(self,name,age,gender):
29         self.name = name
30         self.age = age
31         self.gender = gender
32 
33 f1 = Foo('alex',18,'')
34 print(Foo.__dict__)     ##  'name': <__main__.Typed object at 0x00000000027F1EF0>, 'age': <__main__.Typed object at 0x00000000027F1F98>
35 print(f1.__dict__)      ##  {'name': 'alex', 'age': 18, 'gender': '男'}
用装饰器和描述符给类增加描述符

 

描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

 

7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

 1 class Room:
 2     def __init__(self,name,owner,length,width,highth):
 3         self.房间名 = name
 4         self.主人 = owner
 5         self.长 = length
 6         self.宽 = width
 7         self.高 = highth
 8 
 9     @property     ##  把函数封装起来,让调用者调用的时候感受不到调用的过程
10     def room_area(self):
11         return '%s 住的 %s 面积是 %s平方米'%(self.主人,self.房间名,self.长 *self.宽)
12 
13 r1 = Room('别墅','alex',30,20,10)
14 print(r1.room_area)     ##  alex 住的 别墅 面积是 600平方米
回顾 @property (静态属性)
 1 class CrazyProperty:
 2     def __init__(self,func):
 3         self.func = func
 4 
 5     def __get__(self,instance,owner):   ##  instance--> 实例 , owner--> 类Room
 6         if instance is None:        ##  如果类调用 room_area ,这里返回一个CrazyProperty对象
 7             return self
 8         res = self.func(instance)
 9         return res
10 
11 class Room:
12     def __init__(self,name,owner,length,width,highth):
13         self.房间名 = name
14         self.主人 = owner
15         self.长 = length
16         self.宽 = width
17         self.高 = highth
18 
19     @CrazyProperty      ##  room_area = CrazyProperty(room_area)    ##  定义类装饰器,实际上也是CrazyProperty给类Room添加描述符
20     def room_area(self):
21         return '%s 住的 %s 面积是 %s平方米'%(self.主人,self.房间名,self.长 *self.宽)
22 
23 r1 = Room('别墅','alex',30,20,10)
24 print(r1.room_area)     ##  alex 住的 别墅 面积是 600平方米
25 
26 print(Room.room_area)       ##  <__main__.CrazyProperty object at 0x00000000021B1F98>
自制一个property

但是这样子就有疑问了,类本身就给你提供了一个property方法,你还非得自己整了一个一模一样的,不是闲着蛋疼吗?

下面就完成一个property完成不了的功能:实现延迟计算(也就是说,实例第一次调用完room_area之后,会将room_area存到自己的属性字典里,下次调用就直接在实例属性字典里找,大大缩减时间)

 1 class CrazyProperty:
 2     def __init__(self,func):
 3         self.func = func
 4 
 5     def __get__(self,instance,owner):   ##  instance--> 实例 , owner--> 类Room
 6         print('---------->get')
 7         if instance is None:        ##  如果类调用 room_area ,这里返回一个CrazyProperty对象
 8             return self
 9         res = self.func(instance)
10         setattr(instance,self.func.__name__,res)
11         return res
12 
13 class Room:
14     def __init__(self,name,owner,length,width,highth):
15         self.房间名 = name
16         self.主人 = owner
17         self.长 = length
18         self.宽 = width
19         self.高 = highth
20 
21     @CrazyProperty      ##  room_area = CrazyProperty(room_area)    ##  定义类装饰器,实际上也是CrazyProperty给类Room添加描述符
22     def room_area(self):
23         return '%s 住的 %s 面积是 %s平方米'%(self.主人,self.房间名,self.长 *self.宽)
24 
25 r1 = Room('别墅','alex',30,20,10)
26 print(r1.room_area)     ##  alex 住的 别墅 面积是 600平方米
27 print(r1.__dict__)      ##  {'房间名': '别墅', '主人': 'alex', '长': 30, '宽': 20, '高': 10, 'room_area': 'alex 住的 别墅 面积是 600平方米'}
延迟计算实现

解释一下实现的机制:第一次实例调用room_area,由于实例属性没有,就会找非数据描述符,进行__get__后,get方法将room_area存到实例属性里,下次实例就直接在自己的属性字典里找room_area。

 

 

  • 十五、__enter__ 和 __exit__

我们知道在操作文件对象可以这么写

1 with open('a.txt') as t:
2     '代码块'

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明 __enter__ 和 __exit__ 方法

 1 class Open:
 2     def __init__(self,name):
 3         self.name = name
 4     def __enter__(self):
 5         print('执行enter')
 6         return self
 7     def __exit__(self, exc_type, exc_val, exc_tb):      ##  open逻辑结束后会执行这个方法    ##  实际上就类似于 f.close()
 8         print('执行exit')
 9 
10 with Open('a.txt') as f:  ##    执行enter      ##  实例化,执行类Open下的enter方法,并把返回值赋值给f
11     print(f)        ##  <__main__.Open object at 0x0000000002142FD0>
12     print('---------------------')
13     ##      执行exit
__enter__ 和 __exit__
 1 class Open:
 2     def __init__(self,name):
 3         self.name = name
 4     def __enter__(self):
 5         print('执行enter')
 6         return self
 7     def __exit__(self, exc_type, exc_val, exc_tb):
 8         print('执行exit')
 9         print(exc_type)         ##  异常类
10         print(exc_val)          ##  异常值
11         print(exc_tb)           ##  异常追踪信息
12         return True           ##  吞掉异常,程序不会报错
13 with Open('a.txt') as f:        ##  实例化,执行类Open下的enter方法,并把返回值赋值给f
14     print(f)
15     print(abcdefg)      ##  此时这里有异常
16     print(f.name)       ##  此时上面有异常,直接执行__exit__,执行完毕with语句也就执行完毕,不会执行到这一句以及接下来的语句
17     print('---------------------')
18 print('****************************')
__exit__ 的作用

总结:

一、没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
二、有异常的情况下,从异常出现的位置直接触发 __exit__
a、如果 __exit__ 的返回值为True,代表吞掉了异常
b、如果 __exit__ 的返回值不是True,代表吞出了异常
c、__exit__ 的语句运行完毕代表了整个with语句的执行完毕

用途与好处:

1、使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动干预
2、在需要管理一下资源比如文件,网络连接和锁的编程环境中,可以在 __exit__ 中定制自动释放资源的机制,你无须再去关心这个问题,这将大有用处

 

  • 十六、元类

我们都知道一切皆对象,类的对象是实例,那本质上类也是一个对象,也是另一个类产生的,这个类就是元类(type)。

 1 class Foo:  ##  第一种创建类的方式
 2     pass
 3 print(type(Foo))
 4 
 5 def __init__(self,name):    ##添加构造函数
 6     self.name = name
 7 def test(self):     ##  添加函数
 8     print('执行test')
 9     
10 Ffo = type('Ffo',(object,),{'x':1,'__init__':__init__,'test':test})     ##等同于Foo    ##  第二种创建类的方式
11 
12 f1 = Ffo('alex')
13 print(f1.name)
14 f1.test()
创建类的两种方式

 

 下面自定制一个元类

 1 class MyType(type):
 2     def __init__(cls,a,b,c):
 3         print('元类构造函数开始执行')
 4 
 5     def __call__(cls,*args,**kwargs):       ##  Foo() 执行就会调用call方法
 6         obj = object().__new__(cls)    ##  <__main__.Foo object at 0x00000000027B1128>
 7         cls.__init__(obj,*args,**kwargs)
 8         return obj      ##  重新把Foo产生的对象返回
 9 
10 class Foo(metaclass=MyType):
11     def __init__(self,name):
12         self.name = name
13 
14 # print(Foo)      ##  <class '__main__.Foo'>
15 f1 = Foo('alex')
16 print(f1.__dict__)      ##  {'name': 'alex'}
自制元类

 

posted @ 2020-04-05 15:00  毛新觉罗  阅读(147)  评论(0编辑  收藏  举报