python基础-面向对象进阶

一、什么是反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

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

1、hasattr(object,name)

判断object中有没有一个name字符串对应的方法或属性

2、getattr(object, name, default=None)

 获取属性

3、setattr(x, y, v)

 设置属性

4、delattr(x, y)

  删除属性

示例:

 1 class BlackMedium:
 2     feature='Ugly'
 3     def __init__(self,name,addr):
 4         self.name=name
 5         self.addr=addr
 6 
 7     def sell_house(self):
 8         print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)
 9     def rent_house(self):
10         print('%s 黑中介租房子啦,傻逼才租呢' %self.name)
11 
12 b1=BlackMedium('万成置地','回龙观天露园')
13 
14 #检测是否含有某属性
15 print(hasattr(b1,'name'))
16 print(hasattr(b1,'sell_house'))
17 
18 #获取属性
19 n=getattr(b1,'name')
20 print(n)
21 func=getattr(b1,'rent_house')
22 func()
23 
24 # getattr(b1,'aaaaaaaa') #报错
25 print(getattr(b1,'aaaaaaaa','不存在啊'))
26 
27 #设置属性
28 setattr(b1,'sb',True)
29 setattr(b1,'show_name',lambda self:self.name+'sb')
30 print(b1.__dict__)
31 print(b1.show_name(b1))
32 
33 #删除属性
34 delattr(b1,'addr')
35 delattr(b1,'show_name')
36 #delattr(b1,'show_name111')#不存在,则报错
37 
38 print(b1.__dict__)

执行结果:

1 True
2 True
3 万成置地
4 万成置地  黑中介房子啦,傻逼才租呢
5 不存在啊
6 {'name': '万成置地', 'show_name': <function <lambda> at 0x01142F60>, 'sb': True, 'addr': '回龙观天露园'}
7 万成置地sb

总结:

hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性

三、为什么用反射

有俩程序员,一个alex,一个是egon,alex在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,alex想到了反射,使用了反射机制alex可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现alex想要的功能。

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

示例:

1 class FtpClient:
2     'ftp客户端,但是还么有实现具体的功能'
3     def __init__(self,addr):
4         print('正在连接服务器[%s]' %addr)
5         self.addr=addr
 1 #from module import FtpClient
 2 f1=FtpClient('192.168.1.1')
 3 if hasattr(f1,'get'):
 4     func_get=getattr(f1,'get')
 5     func_get()
 6 else:
 7     print('---->不存在此方法')
 8     print('处理其他的逻辑')
 9 
10 不影响alex的代码编写

四、动态导入模块

1、新建一个t.py的文件

1 print('---------------->')
2 def test1():
3     print('test1')
4 
5 def _test2():
6     print('test2')

2、再创建:m1文件夹,再在他下面创建一个t.py

 1 module_t=__import__('m1.t')
 2 print(module_t)
 3 module_t.t.test1()
 4 # from m1.t import *
 5 # from m1.t import test,_test2
 6 
 7 import importlib
 8 m=importlib.import_module('m1.t')
 9 print(m)
10 m.test1()
11 m._test2()

执行结果:

1 ---------------->
2 <module 'm1' (namespace)>
3 test1
4 <module 'm1.t' from 'D:\python\day26\m1\t.py'>
5 test1
6 test2

五、三个参数,给对象添加属性

__setattr__   添加/修改属性会触发它的执行

__delattr__   删除属性的时候会触发

__getattr__   只有在使用点调用属性且属性不存在的时候才会触发

作用:系统内置函数属性(你定义了就用你定义的函数属性,不定义就用系统默认的函数属性)

综合应用示例:

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __getattr__(self, item):
 7         print('----> from getattr:你找的属性不存在')
 8 
 9 
10     def __setattr__(self, key, value):
11         print('----> from setattr')
12         # self.key=value #这就无限递归了,你好好想想
13         # self.__dict__[key]=value #应该使用它
14 
15     def __delattr__(self, item):
16         print('----> from delattr')
17         # del self.item #无限递归了
18         self.__dict__.pop(item)
19 
20 #__setattr__添加/修改属性会触发它的执行
21 f1=Foo(10)
22 print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
23 f1.z=3
24 print(f1.__dict__)
25 
26 #__delattr__删除属性的时候会触发
27 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
28 del f1.a
29 print(f1.__dict__)
30 
31 #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
32 f1.xxxxxx

分开应用:

示例1:__getattr__(重点,因为常用

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __getattr__(self, item):
 7         print('执行__getattr__')
 8 
 9 f1=Foo(10)
10 print(f1.y)
11 
12 #没有的时候就会触发: __getattr__
13 print(getattr(f1,'y'))  #len(str)---->str.__len__()
14 f1.ssssssssssssssssssssssssssssss

执行结果:

1 10
2 10
3 执行__getattr__

示例2:__delattr__(不常用)

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __delattr__(self, item):
 7         print('删除操作__delattr__')
 8 
 9 f1=Foo(10)
10 del f1.y
11 del f1.x

执行结果:

1 删除操作__delattr__
2 删除操作__delattr__

示例3: __setattr__(不常用)

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __setattr__(self,key,value):
 7         print('__setattr__执行')
 8         #self.key=value
 9         self.__dict__[key]=value
10 
11 #增加键、值到字典中
12 f1=Foo(10)
13 print(f1.__dict__)
14 
15 f1.z=2
16 print(f1.__dict__)

执行结果:

1 __setattr__执行
2 {'y': 10}
3 __setattr__执行
4 {'z': 2, 'y': 10}

总结:

obj点的方式去操作属性时触发的方法

__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发

六、二次加工标准类型(包装)

包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)。

 1 class List(list):
 2     def append(self, p_object):
 3         '我改写的方法'
 4         if not isinstance(p_object,str):
 5             print('只有字符串类型能被添加到列表中')
 6             return
 7         # self.append(p_object) #进入无限递归
 8         super().append(p_object)
 9     def show_mid(self):
10         '我新增的方法'
11         index=int(len(self)/2)
12         print(self[index])
13 
14 
15 l1=List('hello')
16 
17 l1.append('abc')
18 print(l1,type(l1))
19 
20 #数字无法添加成功
21 l1.append(1)
22 print('-->',l1)
23 
24 #基于标准类型新增了功能
25 l1.show_mid()

授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

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

1、通过触发__getattr__方法,找到read方法

示例1:

 1 import time
 2 class FileHandle:
 3     def __init__(self,filename,mode='r',encoding='utf-8'):
 4         self.file=open(filename,mode,encoding=encoding)
 5         self.mode=mode
 6         self.encoding=encoding
 7 
 8     def __getattr__(self, item):
 9           print(item,type(item))
10           self.file.read         #self.file里面有read方法
11           return getattr(self.file,item)  #能过字符串来找到,并通过return返回,就找到了read方法,
12 
13 f1=FileHandle('a.txt','r')
14 print(f1.file)
15 print(f1.__dict__)   #类的字典里,没有read方法,就触发了__getattr__方法
16 print(f1.read)       #找到了read方法
17 
18 sys_f=open('b.txt','w+')
19 print('---->',getattr(sys_f,'read'))   #找到了read方法

执行结果:

1 <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>
2 
3 read <class 'str'>
4 
5 <built-in method read of _io.TextIOWrapper object at 0x01638EB0>
6 
7 {'encoding': 'utf-8', 'file': <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>, 'mode': 'r'}
8 
9 ----> <built-in method read of _io.TextIOWrapper object at 0x01638F30>
View Code

2、往文件里面写入内容

示例2:

 1 import time
 2 class FileHandle:
 3     def __init__(self,filename,mode='r',encoding='utf-8'):
 4         # self.filename=filename
 5         self.file=open(filename,mode,encoding=encoding)
 6         self.mode=mode
 7         self.encoding=encoding
 8     def write(self,line):
 9         print('------------>',line)
10         t=time.strftime('%Y-%m-%d %X')
11         self.file.write('%s %s' %(t,line))
12 
13     def __getattr__(self, item):
14         # print(item,type(item))
15         # self.file.read
16         return getattr(self.file,item)
17 
18 f1=FileHandle('a.txt','w+')
19 f1.write('1111111111111111
')
20 f1.write('cpu负载过高
')
21 f1.write('内存剩余不足
')
22 f1.write('硬盘剩余不足
')

执行结果:

会创建一个a.txt的文件,并往里面写入内容:

1 2016-12-23 18:34:16 1111111111111111
2 2016-12-23 18:34:16 cpu负载过高
3 2016-12-23 18:34:16 内存剩余不足
4 2016-12-23 18:34:16 硬盘剩余不足

七、isinstance(obj,cls)和issubclass(sub,super)

1、isinstance(obj,cls) 检查是否obj是否是类cls的对象

 示例:

1 class Foo(object):
2     pass
3 
4 obj = Foo()
5 
6 print(isinstance(obj,Foo))

执行结果:

1 True

2、issubclass(sub,super)检查sub类是否是supper类的派生类

示例:

1 class Foo(object):
2     pass
3 
4 class Bar(Foo):
5     pass
6 
7 print(issubclass(Bar,Foo))

执行结果:

1 True

八、__getattribute__

 示例1:

不存在的属性访问,就会触发__getattr__方法

 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4 
 5     def __getattr__(self, item):
 6         print('执行的是我')
 7         #return self.__idct__[item]
 8 
 9 f1=Foo(10)
10 print(f1.x)
11 f1.xxxxxx  #不存在的属性访问,触发__getattr__

执行结果:

1 10
2 执行的是我

示例2:

不管是否存在,都会执行__getattribute__方法

 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4 
 5     def __getattribute__(self, item):
 6         print('不管是否存在,我都会执行')
 7 
 8 f1=Foo(10)
 9 f1.x
10 f1.xxxxxxx

执行结果:

1 不管是否存在,我都会执行
2 不管是否存在,我都会执行

示例:3:

 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4 
 5     def __getattr__(self, item):   #相当于监听大哥的异常,大哥抛出导常,他就会接收。
 6         print('执行的是我')
 7         # return self.__dict__[item]
 8 
 9     def __getattribute__(self, item):
10         print('不管是否存在,我都会执行')
11         raise AttributeError('抛出异常了')
12 
13 f1=Foo(10)
14 f1.x         #结果是:10 ,调用会触发系统的
15 f1.xxxxxxx   #如果不存在会触发自己定义的

执行结果:

1 不管是否存在,我都会执行
2 执行的是我
3 不管是否存在,我都会执行
4 执行的是我

九、__setitem__,__getitem,__delitem__  (操作字典就用item的方式)

obj[‘属性’]的方式去操作属性时触发的方法

__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发

 示例1:

 1 class Foo:
 2 
 3     def __getitem__(self, item):
 4         print('getitem',item)
 5         return self.__dict__[item]
 6 
 7     def __setitem__(self, key, value):
 8         print('setitem')
 9         self.__dict__[key]=value
10 
11     def __delitem__(self, key):
12         print('delitem')
13         self.__dict__.pop(key)
14 
15 f1=Foo()
16 print(f1.__dict__)
17 f1['name']='agon'
18 f1['age']=18
19 print('====>',f1.__dict__)
20 
21 del f1['name']
22 print(f1.__dict__)
23 
24 print(f1,['age'])

执行结果:

 1 {}
 2 
 3 setitem
 4 
 5 setitem
 6 
 7 ====> {'age': 18, 'name': 'agon'}
 8 
 9 delitem
10 
11 {'age': 18}
12 
13 <__main__.Foo object at 0x01DC4150> ['age']
View Code

 示例2:

 1 class Foo:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __getitem__(self, item):
 6         print(self.__dict__[item])
 7 
 8     def __setitem__(self, key, value):
 9         self.__dict__[key]=value
10     def __delitem__(self, key):
11         print('del obj[key]时,我执行')
12         self.__dict__.pop(key)
13     def __delattr__(self, item):
14         print('del obj.key时,我执行')
15         self.__dict__.pop(item)
16 
17 f1=Foo('sb')
18 f1['age']=18
19 f1['age1']=19
20 del f1.age1
21 del f1['age']
22 f1['name']='alex'
23 print(f1.__dict__)

执行结果:

1 del obj.key时,我执行
2 
3 del obj[key]时,我执行
4 
5 {'name': 'alex'}
View Code

 十、__str__, __repr__,__format__

1、改变对象的字符串显示__str__,__repr__  (只能是字符串的值,不能是非字符串的值)

示例1:

1 l = list('hello')
2 print(1)
3 
4 file=open('test.txt','w')
5 print(file)

执行结果:

1 1
2 <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
View Code

示例2:

__str__方法

 1 #自制str方法
 2 class Foo:
 3     def __init__(self,name,age):
 4         self.name=name
 5         self.age=age
 6 
 7     def __str__(self):
 8         return '名字是%s 年龄是%s' %(self.name,self.age)
 9 
10 f1=Foo('age',18)
11 print(f1)  #str(f1)---->f1.__str__()
12 
13 x=str(f1)
14 print(x)
15 
16 y=f1.__str__()
17 print(y)

执行结果:

1 名字是age 年龄是18
2 名字是age 年龄是18
3 名字是age 年龄是18
View Code

示例3:

__repe__方法

 1 #触发__repr__方法,用在解释器里输出
 2 class Foo:
 3     def __init__(self,name,age):
 4         self.name=name
 5         self.age=age
 6 
 7     def __repr__(self):
 8         return '名字是%s 年龄是%s' %(self.name,self.age)
 9 
10 f1=Foo('agon',19)
11 print(f1)

执行结果:

1 名字是agon 年龄是19

示例4:

__str__和__repe__ 共存时的用法

 1 # 当str与repr共存时
 2 class Foo:
 3     def __init__(self,name,age):
 4         self.name=name
 5         self.age=age
 6 
 7         def __str__(self):  
 8             return '名字是%s 年龄是%s' % (self.name,self.age)
 9 
10         def __repr__(self):
11             return '名字是%s 年龄是%s' %(self.name,self.age)
12 
13 f1=Foo('egon',19)
14 #repr(f1)--->f1.__repr__()
15 print(f1)  #str(f1)--->f1.__str__()---->f1.__repr__()
执行结果:
1 <__main__.Foo object at 0x018E10B0>
View Code

总结:

str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常 

2、自定制格式化字符串__format__

format的用法

示例1:

1 x = '{0}{0}{0}'.format('dog')
2 print(x)

执行结果:

1 dogdogdog

不用__format__的方式实现

示例2:

 1 class Date:
 2     def __init__(self,year,mon,day):
 3         self.year=year
 4         self.mon=mon
 5         self.day=day
 6 
 7 d1=Date(2016,12,26)
 8 
 9 x = '{0.year}{0.mon}{0.day}'.format(d1)
10 y = '{0.year}:{0.mon}:{0.day}'.format(d1)
11 z = '{0.year}-{0.mon}-{0.day}'.format(d1)
12 print(x)
13 print(y)
14 print(z)

执行结果:

1 20161226
2 2016:12:26
3 2016-12-26

用__format__的方式实现

示例3:

 1 format_dic={
 2     'ymd':'{0.year}:{0.month}:{0.day}',
 3     'm-d-y':'{0.month}-{0.day}-{0.year}',
 4     'y:m:d':'{0.year}:{0.month}:{0.day}',
 5 }
 6 
 7 class Date:
 8     def __init__(self,year,month,day):
 9         self.year=year
10         self.month=month
11         self.day=day
12 
13     def __format__(self, format_spec):
14         print('我执行啦')
15         print('----->',format_spec)
16         if not format_spec or format_spec not in format_dic:
17             format_spec='ymd'
18         fmt=format_dic[format_spec]
19         return fmt.format(self)
20 
21 d1=Date(2016,12,29)
22 # print(format(d1))  #d1.__format__()
23 # print(format(d1))
24 
25 print(format(d1,'ymd'))
26 print(format(d1,'y:m:d'))
27 print(format(d1,'m-d-y'))
28 print(format(d1,'m-d:y'))
29 print('===========>',format(d1,'sdsdddsfdsfdsfdsfdsfsdfdsfsdfds'))

执行结果:

 1 我执行啦
 2 -----> ymd
 3 2016:12:29
 4 我执行啦
 5 -----> y:m:d
 6 2016:12:29
 7 我执行啦
 8 -----> m-d-y
 9 12-29-2016
10 我执行啦
11 -----> m-d:y
12 2016:12:29
13 我执行啦
14 -----> sdsdddsfdsfdsfdsfdsfsdfdsfsdfds
15 ===========> 2016:12:29
View Code

 十一、 __slots__  (慎用)

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

2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

__slots__的作用:节省内存空间

1、一个key的情况

 示例1:

 1 #__slots__  (作用:就是节省内存)
 2 #一个key的值
 3 
 4 class Foo:
 5     __slots__='name'  #定义在类中的类变量,由这个类产生的实例,不在具有__dict__的属性字典,限制了创建属性
 6 
 7 f1=Foo()
 8 f1.name='agon'
 9 print(f1.name)         #只能有一个name属性
10 print(Foo.__slots__)
11 print(f1.__slots__)

 执行结果:

1 agon
2 name
3 name
View Code

2、两个key的情况

示例2:

 1 #两个key的情况
 2 class Foo:
 3     __slots__=['name','age']
 4 
 5 f1=Foo()
 6 
 7 print(Foo.__slots__)
 8 print(f1.__slots__)
 9 f1.name='egon'
10 f1.age=17
11 print(f1.name)
12 print(f1.age)
13 # f1.gender='male'   #会报错,加不上,#AttributeError: 'Foo' object has no attribute 'gender'
14 
15 #只能定义__slots__提供的属性(这里就定义了两个属性)大家都用一个属性字典,优势就是节省内存
16 f2=Foo()
17 print(f2.__slots__)
18 f2.name='alex'
19 f2.age=18
20 print(f2.name)
21 print(f2.age)

执行结果:

1 ['name', 'age']
2 ['name', 'age']
3 egon
4 17
5 ['name', 'age']
6 alex
7 18

十二、__doc__

 1、它类的描述信息

示例:

1 class Foo:
2     '我是描述信息'
3     pass
4 
5 print(Foo.__doc__)

2、该属性无法继承

示例:

 1 #__doc__ 该属性无法继承
 2 
 3 class Foo:
 4     pass
 5 
 6 class Bar(Foo):
 7     pass
 8 
 9 print(Foo.__dict__)  #只要加上了__doc__,该属性就无法继承给子类
10 print(Bar.__dict__)  #原理就是在底层字典里面,会加一个'__doc__': None,

十三、__module__和__class__

  __module__ 表示当前操作的对象在那个模块

  __class__     表示当前操作的对象的类是什么

 1、创建lib/aa.py

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 class C:
5 
6     def __init__(self):
7         self.name = ‘SB'
8 
9 lib/aa.py

2、输出模块和输出类

1 from lib.aa import C
2 
3 obj = C()
4 print obj.__module__     #输出 lib.aa,即:输出模块
5 print obj.__class__      #输出 lib.aa.C,即:输出类

十四、__del__ 析构方法(垃圾回收时自动触发)

  析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

 1 class Foo:
 2     def __init__(self,name):
 3         self.name=name
 4     def __del__(self):
 5         print('我执行啦')
 6 
 7 f1=Foo('alex')
 8 
 9 #del f1  #删除实例会触发__del__
10 del f1.name  #删除实例的属性不会触发__del__
11 print('---------------->')

执行结果:

1 ---------------->
2 我执行啦

十五、__call__

示例:

1 class Foo:
2     def __call__(self, *args, **kwargs):
3         print('实例执行啦 obj()')
4 
5 f1=Foo()  #Foo下的__call__
6 
7 f1()  #abc下的__call__

执行结果:

1 实例执行啦 obj()

十六、 __next__和__iter__实现迭代器协议

一、什么是迭代器协议

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)

3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

二、python中强大的for循环机制

for循环的本质:循环所有对象,全都是使用迭代器协议。

(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环式,调用了他们内部的__iter__方法,把他们变成了可迭代对象

然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉StopIteration异常,以终止迭代。

示例1:

1 #迭代器协议
2 
3 class Foo:
4     pass
5 
6 l = list('hello')
7 for i in l:   #for循环本质就是调用他:f1.__iter__() == iter(f1)
8     print(i)

执行结果:

1 h
2 e
3 l
4 l
5 o
View Code

示例2:

 1 class Foo:
 2     def __init__(self,n):
 3         self.n=n
 4 
 5     def __iter__(self):  #把一个对象就成一个可迭代对象,必须有__iter__
 6         return self
 7 
 8     def __next__(self):
 9         self.n+=1
10         return self.n
11 
12 f1=Foo(10)
13 # for i in f1:  #for循环本质就是调用他:f1.__iter__() == iter(f1)
14 #     print(i)
15 
16 print(f1.__next__())
17 print(next(f1))
18 print(next(f1))
19 print(next(f1))
20 print(next(f1))
21 print(next(f1))
22 
23 
24 for i in f1:  #for循环本质就是调用他:f1.__iter__() == iter(f1)
25     print(i)

执行结果:

 1 11
 2 12
 3 13
 4 14
 5 15
 6 16
 7 17
 8 18
 9 19
10 20
11 21
12 22
13 23
14 24
15 25
16 26
17 会一直无限循环下去.....
18 下面部分省略.....
View Code

示例3:

 1 class Foo:
 2     def __init__(self,n):
 3         self.n=n
 4 
 5     def __iter__(self):  #把一个对象就成一个可迭代对象,必须有__iter__
 6         return self
 7 
 8     def __next__(self):
 9         if self.n == 13:
10             raise StopIteration('终止了')
11         self.n+=1
12         return self.n
13 
14 
15 f1=Foo(10)
16 
17 # print(f1.__next__())
18 # print(f1.__next__())
19 # print(f1.__next__())
20 # print(f1.__next__())
21 
22 for i in f1:  #f1.__iter__() == iter(f1)
23     print(i)  #obj.__next__()

执行结果:

1 11
2 12
3 13
View Code

三、斐波那契数列

什么是斐波那契数列?

  斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946........
这个数列从第3项开始,每一项都等于前两项之和。
 
示例1:

1 1 2 (相当于1+1=2) #后面这个数是前两个数之和

3 5 8 (相当于3+5=8)   #后面这个数是前两个数之和

用迭代器协议的方法实现:一次产生一个值

示例2:

 1 #斐波那契数列
 2 class Fib:
 3     def __init__(self):
 4         self._a=1
 5         self._b=1
 6 
 7     def __iter__(self):
 8         return self
 9     def __next__(self):
10         if self._a > 100:
11             raise StopIteration('终止了')  # >100 就抛出异常
12         self._a,self._b=self._b,self._a + self._b   #1+1=b; a,b=b,a(等于交换值)
13         return self._a
14 
15 f1=Fib()
16 print(next(f1))
17 print(next(f1))
18 print(next(f1))
19 print(next(f1))
20 print(next(f1))
21 print('==================================')
22 for i in f1:
23     print(i)

执行结果:

 1 1
 2 2
 3 3
 4 5
 5 8  #print(next(f1)) 
 6 ==================================
 7 13 #for循环的值
 8 21
 9 34
10 55
11 89
12 144

十七、描述符(__get__,__set__,__delete__)  (新式类中描述符在大型开发中常用,必须掌握。)

描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()三个方法中的一个,这也被称为描述符协议。

 1 class 描述符:
 2     def __get__():
 3         pass
 4     def __set__():
 5         pass
 6     def __delete__():
 7         pass
 8 
 9 class 类:
10     name=描述符()
11 
12 obj=类()
13 obj.name        #get方法
14 obj.name='egon' #set方法
15 del obj.name    #delete

描述符的三种方法:

__get__():  .调用一个属性时,触发
__set__():   .为一个属性赋值时,触发
__delete__():  采用del.删除属性时,触发

1、定义一个描述符

示例1:

1 class Foo:   #在python3中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方法')

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

示例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 print(f1.name)
13 del f1.name
14 #疑问:何时,何地,会触发这三个方法的执行

3、描述符应用之何时?何地?

示例3:

 1 #描述符应用之何时?何地?
 2 
 3 #描述符Str
 4 class Foo:
 5     def __get__(self,instance,owner):
 6         print('===>get方法')
 7     def __set__(self, instance, value):
 8         print('===>set方法')
 9     def __delete__(self, instance):
10         print('===>delete方法')
11 
12 
13 class Bar:
14     x=Foo() #在何地?
15 
16 print(Bar.__dict__)
17 
18 #在何时?
19 b1=Bar()
20 # b1.x                 #调用就会触发上面的get方法
21 # b1.x=1               #赋值
22 # del b1.x
23 print(b1.x)            #触发了描述器里面的get方法,得到None
24 b1.x=1                 #触发了描述器里面的set方法,得到{}
25 print(b1.__dict__)     #写到b1的属性字典中
26 
27 del b1.x               #打印===>delete方法

执行结果:

 1 {'x': <__main__.Foo object at 0x014210D0>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__doc__': None, '__module__': '__main__'}
 2 
 3 ===>get方法
 4 
 5 None
 6 
 7 ===>set方法
 8 
 9 {}
10 
11 ===>delete方法
View Code

4、描述符分两种

一、数据描述符:至少实现了__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')

5、注意事项:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类
二、必须把描述符定义成另外一个类触发的类属性,不能为定义到构造函数中

示例:

 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 Bar:
10     x=Foo() #定义一个描述符
11     def __init__(self,n):
12         self.x=n
13 
14 b1=Bar(10)   #触发set方法
15 print(b1.__dict__)

执行结果:

1 ===>set方法
2 {}

三、要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

类属性>数据描述符

示例:

 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 Bar:
10     x=Foo()   #调用foo()属性,会触发get方法
11 
12 print(Bar.x)  #类属性比描述符有更高的优先级,会触发get方法
13 Bar.x=1       #自己定义了一个类属性,并赋值给x,跟描述符没有关系,所以他不会触发描述符的方法
14 # print(Bar.__dict__)
15 print(Bar.x)

执行结果:

1 ===>get方法
2 None
3 1

数据描述符>实例属性

示例1:

 1 #有get,set就是数据描述符,数据描述符比实例属性有更高的优化级
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6     def __set__(self, instance, value):
 7         print('===>set方法')
 8     def __delete__(self, instance):
 9         print('===>delete方法')
10 
11 class Bar:
12     x = Foo()  # 调用foo()属性,会触发get方法
13 
14 b1=Bar()   #在自己的属性字典里面找,找不到就去类里面找,会触发__get__方法
15 b1.x       #调用一个属性的时候触发get方法
16 b1.x=1     #为一个属性赋值的时候触发set方法
17 del b1.x   #采用del删除属性时触发delete方法

执行结果:

1 1 ===>get方法
2 2 ===>set方法
3 3 ===>delete方法

示例2:

 1 class Foo:
 2     def __get__(self,instance,owner):
 3         print('===>get方法')
 4 
 5     def __set__(self, instance, value):
 6         pass
 7 
 8 class Bar:
 9     x = Foo()
10 
11 b1=Bar()
12 b1.x=1        #触发的是非数据描述符的set方法
13 print(b1.__dict__) 

执行结果:

1 {}   #数据描述符>实例属性

类属性>数据描述符>实例属性

 1 #类属性>数据描述符>实例属性
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6     def __set__(self, instance, value):
 7         print('===>set方法')
 8     def __delete__(self, instance):
 9         print('===>delete方法')
10 
11 class Bar:
12     x = Foo()             #调用foo()属性,会触发get方法
13 
14 b1=Bar()                  #实例化
15 Bar.x=11111111111111111   #不会触发get方法
16 b1.x                      #会触发get方法
17 
18 del Bar.x                 #已经给删除,所以调用不了!报错:AttributeError: 'Bar' object has no attribute 'x'
19 b1.x

非数据描述符

示例1:

 1 #非数据描述符没有set方法
 2 class Foo:
 3     def __get__(self,instance,owner):
 4         print('===>get方法')
 5 
 6     def __delete__(self, instance):
 7         print('===>delete方法')
 8 
 9 class Bar:
10     x = Foo()
11 
12 b1=Bar()
13 b1.x     #自己类中没有,就会去Foo类中找,所以就会触发__get__方法

执行结果:

===>get方法

示例2:

 1 #实例属性>非数据描述符
 2 class Foo:
 3     def __get__(self,instance,owner):
 4         print('===>get方法')
 5 
 6 class Bar:
 7     x = Foo()
 8 
 9 b1=Bar()
10 b1.x=1
11 print(b1.__dict__)  #在自己的属性字典里面,{'x': 1}

执行结果:

1 {'x': 1}

非数据描述符>找不到

 1 #非数据描述符>找不到
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6 
 7 class Bar:
 8     x = Foo()
 9     def __getattr__(self, item):
10         print('------------>')
11 
12 b1=Bar()
13 b1.xxxxxxxxxxxxxxxxxxx    #调用没有的xxxxxxx,就会触发__getattr__方法

执行结果:

1 ------------>    #解发__getattr__方法

四、描述符应用

示例1:

 1 class Typed:   #有__get__,__set__,__delete__ 就是:数据描述符
 2     def __get__(self, instance,owner):
 3         print('get方法')
 4         print('instance参数【%s】' %instance)
 5         print('owner参数【%s】' %owner)
 6 
 7     def __set__(self, instance, value):
 8         print('set方法')
 9         print('instance参数【%s】' %instance)
10         print('value参数【%s】' %value)
11 
12     def __delete__(self, instance):
13         print('delete方法')
14         print('instance参数【%s】'% instance)
15 
16 
17 class People:
18     name=Typed()    #设置代理(代理的就是name属性)
19     def __init__(self,name,age,salary):
20         self.name=name        #触发的是代理
21         self.age=age
22         self.salary=salary
23 
24 p1=People('alex',13,13.3)
25 #'alex'             #触发set方法
26 p1.name             #触发get方法,没有返回值
27 p1.name='age'       #触发set方法
28 print(p1.__dict__)  #{'salary': 13.3, 'age': 13}

执行结果:

 1 set方法
 2 instance参数【<__main__.People object at 0x006110F0> 3 value参数【alex】
 4 get方法
 5 instance参数【<__main__.People object at 0x006110F0> 6 owner参数【<class '__main__.People'> 7 set方法
 8 instance参数【<__main__.People object at 0x006110F0> 9 value参数【age】
10 {'salary': 13.3, 'age': 13}
View Code

示例2:给字典属性传值

 1 #给字典里面传入值
 2 class Typed:
 3     def __init__(self,key):
 4         self.key=key
 5 
 6     def __get__(self, instance, owner):
 7         print('get方法')
 8         return instance.__dict__[self.key]   #触发get方法,会返回字典的值
 9 
10     def __set__(self, instance, value):
11         print('set方法')
12         instance.__dict__[self.key]=value     #存在p1的属性字典里面
13 
14     def __delete__(self, instance):
15         print('delete方法')
16         instance.__dict__.pop(self.key)
17 
18 class People:
19     name=Typed('name')      #name属性被Typed给代理了,t1._set_() self._set__()
20     def __init__(self,name,age,salary):
21         self.name=name
22         self.age=age
23         self.salary=salary
24 
25 p1=People('alex',13,13.3)
26 
27 #打印实例字典
28 print(p1.__dict__)
29 
30 #创建属性,给name赋值,相当于修改了name字典的值
31 p1.name='egon'             #触发的是set方法
32 print(p1.__dict__)
33 
34 #删除name属性
35 print(p1.__dict__)
36 del p1.name               #触发的是delete方法
37 print(p1.__dict__)

执行结果:

 1 #打印实例属性字典
 2 set方法
 3 {'age': 13, 'salary': 13.3, 'name': 'alex'}   
 4 
 5 #修改name字典的值
 6 set方法
 7 {'age': 13, 'salary': 13.3, 'name': 'egon'}
 8 
 9 #删除name属性
10 delete方法
11 {'age': 13, 'salary': 13.3}

示例3:

实现类型检测的两种方法:

方法一:用return的方式

 1 #判断他传入的值是不是字符串类型
 2 class Typed:
 3     def __init__(self,key):
 4         self.key=key
 5 
 6     def __get__(self, instance, owner):
 7         print('get方法')
 8         return instance.__dict__[self.key]   #触发get方法,会返回字典的值
 9 
10     def __set__(self, instance, value):
11         print('set方法')
12         if not isinstance(value,str):
13             print('你传入的类型不是字符串,错误')
14             return   #return的作用就是终止这个属性字典,让他的值设置不进字典中。
15         instance.__dict__[self.key]=value     #存在p1的属性字典里面
16 
17     def __delete__(self, instance):
18         print('delete方法')
19         instance.__dict__.pop(self.key)
20 
21 class People:
22     name=Typed('name')                #name属性被Typed给代理了,t1._set_() self._set__()
23     def __init__(self,name,age,salary):
24         self.name=name
25         self.age=age
26         self.salary=salary
27 
28 #正常的情况下__dict__里面有没有'name':'alex'
29 p1=People('alex',13,13.3)
30 print(p1.__dict__)   #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13}
31 
32 #不正常的情况下,修改'name':213 不等于字符串,改成了int类型
33 p1=People(213,13,13.3)
34 print(p1.__dict__)   #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13}

执行结果:

1 set方法
2 {'salary': 13.3, 'age': 13, 'name': 'alex'}
3 
4 set方法
5 你传入的类型不是字符串,错误
6 {'salary': 13.3, 'age': 13}

方法二:用raise抛出异常的方式,判断他传入的值是不是字符串类型,(写死了,不灵活)

 1 #用抛出异常的方式,判断他传入的值是不是字符串类型
 2 class Typed:
 3     def __init__(self,key):
 4         self.key=key
 5 
 6     def __get__(self, instance, owner):
 7         print('get方法')
 8         return instance.__dict__[self.key]   #触发get方法,会返回字典的值
 9 
10     def __set__(self, instance, value):
11         print('set方法')
12         if not isinstance(value,str):  #判断是否是字符串类型
13             #方法一:return的方式
14             # print('你传入的类型不是字符串,错误')
15             # return   #return的作用就是终止这个属性字典,让他的值设置不进字典中。
16             #方法二:
17             raise TypeError('你传入的类型不是字符串')   ##用抛出异常的方式,判断他传入的值是不是字符串类型
18         instance.__dict__[self.key]=value     #存在p1的属性字典里面
19 
20     def __delete__(self, instance):
21         print('delete方法')
22         instance.__dict__.pop(self.key)
23 
24 class People:
25     name=Typed('name')                #name属性被Typed给代理了,t1._set_() self._set__()
26     def __init__(self,name,age,salary):
27         self.name=name
28         self.age=age
29         self.salary=salary
30 
31 #正常的情况下__dict__里面有没有'name':'alex'
32 # p1=People('alex',13,13.3)
33 # print(p1.__dict__)   #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13}
34 
35 #不正常的情况下,修改'name':213 不等于字符串,改成了int类型
36 p1=People(213,13,13.3)
37 print(p1.__dict__)   #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13}

执行结果:

 1 Traceback (most recent call last):
 2 set方法
 3   File "D:/python/day28/5-1.py", line 219, in <module>
 4 {'name': 'alex', 'age': 13, 'salary': 13.3}
 5 set方法
 6     p1=People(213,13,13.3)
 7   File "D:/python/day28/5-1.py", line 210, in __init__
 8     self.name=name
 9   File "D:/python/day28/5-1.py", line 200, in __set__
10     raise TypeError('你传入的类型不是字符串')
11 TypeError: 你传入的类型不是字符串

类型检测加强版

 示例:4:用raise抛出异常的方式,判断传入的值是什么类型,同时可以判断多个属性  (推荐写法)

 1 #用抛出异常的方式,判断他传入的值是什么类型  (不写死的方式,判断传入值的类型)
 2 class Typed:
 3     def __init__(self,key,expected_type):
 4         self.key=key
 5         self.expected_type=expected_type
 6 
 7     def __get__(self, instance, owner):
 8         print('get方法')
 9         return instance.__dict__[self.key]   #触发get方法,会返回字典的值
10 
11     def __set__(self, instance, value):
12         print('set方法')
13         if not isinstance(value,self.expected_type):
14              raise TypeError('%s 你传入的类型不是%s' %(self.key,self.expected_type)) #用抛出异常的方式,判断他传入的值是什么类型,同时可以判断多个属性的类型
15         instance.__dict__[self.key]=value     #存在p1的属性字典里面
16 
17     def __delete__(self, instance):
18         print('delete方法')
19         instance.__dict__.pop(self.key)
20 
21 class People:
22     name=Typed('name',str)  #name设置代理Typed
23     age=Typed('age',int)    #age设置代理Typed
24     def __init__(self,name,age,salary):
25         self.name=name    #alex传给代理,会触发set方法
26         self.age=age      #age传给代理,会触发set方法
27         self.salary=salary
28 
29 #name是字符串,age是整型,salary必须是浮点数
30 #正确的方式
31 p1=People('alex',13,13.3)
32 
33 #传入错误的类型,会判断传入值的类型
34 #name要求传入的srt,但这里传入的是整型,所以会报错,说你传入的不是srt类型
35 p1=People(213,13,13.3)

执行结果:

 1 set方法
 2   File "D:/python/day28/5-1.py", line 220, in <module>
 3     p1=People(213,13,13.3)
 4 set方法
 5   File "D:/python/day28/5-1.py", line 210, in __init__
 6 set方法
 7     self.name=name
 8   File "D:/python/day28/5-1.py", line 199, in __set__
 9     raise TypeError('%s 你传入的类型不是%s' %(self.key,self.expected_type)) #用抛出异常的方式,判断他传入的值是什么类型
10 
11 TypeError: name 你传入的类型不是<class 'str'>
View Code

十八、__enter__和__exit__

1、操作文件写法

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

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

 1 class Open:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __enter__(self):
 6         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
 7         # return self
 8     def __exit__(self, exc_type, exc_val, exc_tb):
 9         print('with中代码块执行完毕时执行我啊')
10 
11 
12 with Open('a.txt') as f:   #with语句,触发__enter__,返回值赋值给f
13     print('=====>执行代码块')
14     # print(f,f.name)

执行结果:

1 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
2 =====>执行代码块
3 with中代码块执行完毕时执行我啊

3、执行代码块

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都会执行

 1 class Foo:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __enter__(self):
 6         print('执行enter')   
 7         return self        #2、拿到的结果是self,并赋值给f
 8 
 9     def __exit__(self, exc_type, exc_val, exc_tb):  #4、触发__exit__,然后执行print()
10         print('执行exit')
11         print(exc_type)
12         print(exc_val)
13         print(exc_tb)
14         
15 
16 with Foo('a.txt') as f:  #1、with触发的是__enter__,拿到的结果是self并赋值给f; 
17     print(f)             #3、然后会执行with代码块,执行完毕后
18     print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad)  
19     print(f.name)      
20 print('000000000000000000000000000000000000000000000000000000') 

执行结果:

 1 执行enter
 2 Traceback (most recent call last):
 3 <__main__.Foo object at 0x01478A90>
 4   File "D:/python/day28/s1.py", line 56, in <module>
 5 执行exit
 6     print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad)   #触发__exit__
 7 <class 'NameError'>
 8 NameError: name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined
 9 name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined
10 <traceback object at 0x01484710>
View Code

4、有返回值

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

 1 class Foo:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __enter__(self):
 6         print('执行enter')   
 7         return self
 8                        '''class、异常值、追踪信息'''
 9     def __exit__(self, exc_type, exc_val, exc_tb):   #2、有异常的时候,就会触发__exit__方法
10         print('执行exit')
11         print(exc_type)
12         print(exc_val)
13         print(exc_tb)
14         return True         #3、没有return True就会报错,如果有return True异常自己吃了,不报异常
15 
16 with Foo('a.txt') as f:
17     print(f)                
18     print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad)    #1、有异常的情况,他就会触发__exit__
19     print(f.name)           #不执行这行,直接打印下面那行
20 print('000000000000000000000000000000000000000000000000000000')     #4、最后打印这行

执行结果:

1 执行enter
2 <__main__.Foo object at 0x016D8A90>
3 执行exit
4 <class 'NameError'>
5 name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined
6 <traceback object at 0x016E5710>
7 000000000000000000000000000000000000000000000000000000
View Code

总结:

with obj as f:
      '代码块'

1.with obj ---->触发obj.__enter__(),拿到返回值

2.as f----->f=返回值、

3.with obj as f 等同于 f=obj.__enter__()

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

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件网络连接(TCP协议建连接、传输数据、关连接)锁(进程,线程)的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。

十九、类的装饰器

示例1:类的装饰器基本原理

1 #示例1
2 def deco(func):   #高阶函数
3     print('===================')
4     return func  #fuc=test
5 
6 @deco    #装饰器 test=deco(test)
7 def test():
8     print('test函数运行')
9 test()   #运行test

执行结果:

1 ===================
2 test函数运行

示例2:类的装饰器基本原理

 1 def deco(obj):
 2     print('============',obj)
 3     obj.x=1   #增加属性
 4     obj.y=2
 5     obj.z=3
 6     return obj
 7 
 8 @deco   #Foo=deco(Foo)   #@deco语法糖的基本原理(语法糖可以在函数前面加,也可以在类的前面加)
 9 class Foo:  #类的装饰器
10     pass
11 
12 print(Foo.__dict__)  #加到属性字典中

执行结果:

1 ============ <class '__main__.Foo'>
2 {'__module__': '__main__', 'z': 3, 'x': 1, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'y': 2}

用法总结:@deco语法糖可以在函数前面加,也可以在类的前面加

示例3:一切皆对象

 1 def deco(obj):
 2     print('==========',obj)
 3     obj.x=1
 4     obj.y=2
 5     obj.z=3
 6     return obj
 7 
 8 # @deco    #Foo=deco(test)
 9 def test():
10     print('test函数')
11 test.x=1   #但没有人会这么做,只是验证一切皆对象
12 test.y=1
13 print(test.__dict__)

执行结果:

1 {'x': 1, 'y': 1}

示例4:直接传值并加到字典中

 1 def Typed(**kwargs): #负责接收参数
 2     def deco(obj):   #局部作用域
 3         obj.x=1  
 4         obj.y=2  
 5         obj.z=3
 6         return obj
 7     print('====>',kwargs)  #外层传了个字典进来,就是: 'y': 2, 'x': 1,
 8     return deco  
 9 
10 @Typed(x=1,y=2,z=3)        #typed(x=1,y=2,z=3)--->deco 函数名加()就是运行Typed函数 
11 class Foo: 
12     pass

执行结果:

1 ====> {'y': 2, 'x': 1, 'z': 3}

示例5:类的装饰器增强版

 1 def Typed(**kwargs):  #Type传过来的参数,就是name='egon',kwargs包含的就是一个字典
 2     def deco(obj):
 3         for key,val in kwargs.items():  #key=name,val=age
 4             setattr(obj,key,val)        #obj=类名,key=name,val=age
 5         return obj   #返回类本身:obj,就相当于给类加了个属性
 6     return deco
 7 
 8 @Typed(x=1,y=2,z=3)  #1、typed(x=1,y=2,z=3)--->deco 2、@deco--->Foo=deco(Foo)
 9 class Foo:
10     pass
11 print(Foo.__dict__)
12 
13 @Typed(name='egon')  #@deco---->Bar=deco(Bar),重新赋值给了Bar
14 class Bar:
15     pass
16 print(Bar.name)      #最后打印name,就得到egon

执行结果:

1 {'y': 2, '__dict__': <attribute '__dict__' of 'Foo' objects>, 'z': 3, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__module__': '__main__', 'x': 1, '__doc__': None}
2 
3 egon

实现:类型检测控制传入的值是什么类型

1、用raise抛出异常的方式实现

2、用类加装饰器实现 (这种方法更高级

示例6:类加装饰器的应用

示例如下:

1、给类加装饰器,实现控制传入的类型(推荐写法)  可以参考:  示例:4:用raise抛出异常的方式,判断传入的值是什么类型,同时可以判断多个属性。

 1 #给类加装饰器,实现控制传入的类型
 2 
 3 class Typed:
 4     def __init__(self,key,expected_type):
 5         self.key=key
 6         self.expected_type=expected_type
 7 
 8     def __get__(self, instance, owner):
 9         print('get方法')
10         return instance.__dict__[self.key]
11 
12     def __set__(self, instance, value):
13         print('set方法')
14         if not isinstance(value,self.expected_type):
15             raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type))
16         instance.__dict__[self.key]=value
17 
18     def __delete__(self, instance):
19         print('delete方法')
20         instance.__dict__.pop(self.key)
21 
22 def deco(**kwargs):       #kwargs={'name':str,'age':int}
23     def wrapper(obj):     #obj=People
24         for key,val in kwargs.items():    #(('name',str),('age',int))
25             # print(obj,key)
26             setattr(obj,key,Typed(key,val))    #给People设置类属性
27         return obj
28     return wrapper
29 
30 #给类加装饰器,加了装饰器,指定了什么类型,就必须传入什么类型的值,否则就会报错
31 @deco(name=str,age=int,salary=float)  #@wrapper ===>People=wrapper(People)  #实现这个功能的重点在这里
32 class People:
33     # name=Typed('name',int)
34     def __init__(self,name,age,salary):
35         self.name=name
36         self.age=age
37         self.salary=salary
38 
39 #name=srt,age=int,salary=float
40 #传入的是正确的类型,所以不会报错。
41 p1 = People('alex', 13, 13.3)
42 print(People.__dict__)
43 
44 #age设置成了int型,我们传入的是字符串类型,所以会报错:TypeError: age 传入的类型不是<class 'int'>
45 # p1=People('alex','13',13.3)
46 # print(People.__dict__)

执行结果:

1 #传入正确类型的值
2 
3 set方法
4 set方法
5 set方法
6 {'__doc__': None, 'name': <__main__.Typed object at 0x010E3290>, '__dict__': <attribute '__dict__' of 'People' objects>, '__module__': '__main__', '__init__': <function People.__init__ at 0x010EB228>, 'salary': <__main__.Typed object at 0x010E3270>, '__weakref__': <attribute '__weakref__' of 'People' objects>, 'age': <__main__.Typed object at 0x010E32B0>}
7 
8 #传入错误类型的值,会报错,并提示你传入值的类型。

示例7:利用描述自定制property

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         print('===========>',func)
 4         self.func=func
 5     def __get__(self,instance,owner):
 6         print('get')
 7         print('instance')
 8         print('owner')
 9         res=self.func(instance)
10         return res
11 class Room:
12     def __init__(self,name,width,length):
13         self.name=name
14         self.width=width
15         self.length=length
16     @Lazyproperty
17     def area(self):
18         return self.width * self.length
19 
20 r1=Room('厕所',1,1)
21 print(r1.area)

执行结果:

1 get
2 instance
3 owner
4 1

示例8:利用描述符实现延迟计算

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         print('===========>',func)
 4         self.func=func
 5     def __get__(self,instance,owner):
 6         print('get')
 7         print('instance')
 8         print('owner')
 9         res=self.func(instance)
10         return res
11 class Room:
12     def __init__(self,name,width,length):
13         self.name=name
14         self.width=width
15         self.length=length
16     @Lazyproperty
17     def area(self):
18         return self.width * self.length
19 
20 r1=Room('厕所',1,1)
21 print(r1.area)

示例9:非数据描述符

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         self.func=func
 4 
 5     def __get__(self, instance, owner):
 6         print('get')
 7 
 8         if instance is None:
 9             return self
10         res=self.func(instance)
11         setattr(instance,self.func.__name__,res)
12         return res
13 
14 class Room:
15     def __init__(self,name,width,length):
16         self.name=name
17         self.width=width
18         self.length=length
19 
20     @Lazyproperty
21     def area(self):
22         return self.width * self.length
23     @property
24     def areal(self):
25         return self.width * self.length
26 
27 r1=Room('厕所',1,1)
28 
29 print(r1.area)
30 print(r1.__dict__)
31 
32 print(r1.area)
33 print(r1.area)
34 print(r1.area)
35 print(r1.area)
36 print(r1.area)
37 print(r1.area)
38 print(r1.area)
39 print(r1.area)

执行结果:

 1 get
 2 1
 3 {'area': 1, 'length': 1, 'name': '厕所', 'width': 1}
 4 1
 5 1
 6 1
 7 1
 8 1
 9 1
10 1
11 1

二十、元类(metaclass)

1、示例:

1 class Foo:
2     pass
3 
4 f1=Foo() #f1是通过Foo类实例化的对象

python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)

上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

 1 class Foo:
 2     pass
 3 
 4 f1=Foo()
 5 
 6 print(type(f1))  #<class '__main__.Foo'>  
 7 print(type(Foo)) #类的类就是<class 'type'>
 8 
 9 class Bar:
10     pass   
11 print(type(Bar))   #<class 'type'>

执行结果:

1 #type函数可以查看类型,也可以用来查看对象的类,二者是一样的。
2 <class '__main__.Foo'>  
3 <class 'type'> 
4 <class 'type'>

2、什么是元类?

元类是类的类,是类的模板

元类是用来控制如何创建类的,正如类是创建对象的模板一样

元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例Foo类是 type 类的一个实例)

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

3、创建类的两种方式

方式一:

1 class Foo:
2     pass
3 print(Foo)
4 print(Foo.__dict__)

执行结果:

1 <class '__main__.Foo'>
2 {'__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>}

方式二:

 1 #type就是类的类,用type()实例化的结果,就是产生一个类
 2 #用type和class生成的类是一样的效果。
 3 
 4 def __init__(self,name,age):
 5     self.name=name
 6     self.age=age
 7 
 8 def test(self):
 9     print('=======执行的是test=====>')
10 
11 FFo=type('FFo',(object,),{'x':1,'__init__':__init__,'test':test})
12 print(FFo)
13 # print(FFo.__dict__)
14 
15 f1=FFo('alex',18)
16 print(f1.x)         #调的是类属性
17 print(f1.name)      #调name
18 f1.test()           #调方法

执行结果:

1 <class '__main__.FFo'>
2 1
3 alex
4 =======执行的是test=====>

4、一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类。 

示例:  自定制元类

 1 class MyType(type):
 2     def __init__(self,a,b,c):
 3         print('元类的构造函数执行')
 4 
 5     def __call__(self, *args, **kwargs):
 6         obj=object.__new__(self)            #object.__new__(Foo)-->f1
 7         self.__init__(obj,*args,**kwargs)   #Foo.__init__(f1,*arg,**kwargs)
 8         return obj
 9 
10 class Foo(metaclass=MyType):   #Foo=MyType(Foo,'Foo',(),{})---》__init__
11     def __init__(self,name):
12         self.name=name          #f1.name=name
13 f1=Foo('alex')

执行结果:

1 元类的构造函数执行
原文地址:https://www.cnblogs.com/nulige/p/6215005.html