python 面向对象高级应用(三)
目录:
- isinstance(obj,cls)和issubclass(sub,super)
- 反射
- __setattr__,__delattr__,__getattr__
- 二次加工标准类型(包装)
- __getattribute__
- 描述符(__get__,__set__,__delete__)
- property
- __setitem__,__getitem,__delitem__
- __str__,__repr__,__format__
- __slots__
- __next__和__iter__实现迭代器协议
- __doc__
- __module__和__class__
- __del__
- __enter__和__exit__
- __call__
- metaclass
一、isinstance(obj,cls)和issubclass(sub,super)
1、isinstance(obj,cls)检查是否obj是否是类 cls 的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 isinstance(obj, Foo)
2、issubclass(sub, super)检查sub类是否是 super 类的派生类
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
二、反射
1、反射定义:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2、python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数:
hasatter(object, name) : 判断object中有没有一个name字符串对应的方法或属性,检查成员
getattr(object, name, default=None): 获取成员
setattr(x, y, v):设置成员
delattr(x, y):删除成员
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'abc' 5 6 def func(self): 7 return 'ok' 8 9 obj = Foo() 10 #获取成员 11 ret = getattr(obj, 'func')#获取的是个对象 12 r = ret() 13 print(r) 14 #检查成员 15 ret = hasattr(obj,'func')#因为有func方法所以返回True 16 print(ret) 17 #设置成员 18 print(obj.name) #设置之前为:abc 19 ret = setattr(obj,'name',19) 20 print(obj.name) #设置之后为:19 21 #删除成员 22 print(obj.name) #abc 23 delattr(obj,'name') 24 print(obj.name) #报错
a.应用实例(一):使用格式
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__)
b.应用实例(二):类也是对象,即可以反射
1 class Foo(object): 2 3 staticField = "old boy" 4 5 def __init__(self): 6 self.name = 'wupeiqi' 7 8 def func(self): 9 return 'func' 10 11 @staticmethod 12 def bar(): 13 return 'bar' 14 15 print getattr(Foo, 'staticField') 16 print getattr(Foo, 'func') 17 print getattr(Foo, 'bar')
c.应用实例(三):反射当前模块成员
1 import sys 2 3 4 def s1(): 5 print 's1' 6 7 8 def s2(): 9 print 's2' 10 11 12 this_module = sys.modules[__name__] 13 14 hasattr(this_module, 's1') 15 getattr(this_module, 's2')
d.应用实例(四):导入其他模块,利用反射查找该模块是否存在某个方法
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def test(): 5 print('from the test')
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 """ 5 程序目录: 6 module_test.py 7 index.py 8 9 当前文件: 10 index.py 11 """ 12 13 import module_test as obj 14 15 #obj.test() 16 17 print(hasattr(obj,'test')) 18 19 getattr(obj,'test')()
3、反射的优点
a.实现可插拔机制.
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
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('处理其他的逻辑')
b.动态导入模块(基于反射当前模块成员).
1 ''' 2 动态导入模块 3 路径 4 |-import_lib 5 --|-metaclass.py 6 ''' 7 import importlib # 导入importlib内置库 8 9 importlib.import_module("import_lib.metaclass") #得到上述路径文件
三、__setattr__,__delattr__,__getattr__
类的属性操作,__dict__的重写方法。可修改
1、__setattr__,__delattr__,__getattr__(等同于setattr,delattr,getattr),它的用法如下:
__setattr__ 添加/修改属性会触发执行
__delattr__ 删除属性的时候会触发
__getlattr__ 只有在使用点调用属性且属性不存在的时候才会触发。(新式类独有)
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、包装:
python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)如:
1 # List继承列表类,他继承了python解释器list列表的所有功能 2 # class List(list): 3 # pass 4 # 5 # l=List([1,2,3]) 6 # print(l) 7 # l.append(4) 8 # print(l)
通过继承,可修改添加为:
1 class List(list): 2 def __init__(self,item,tag=False): 3 super().__init__(item) 4 self.tag=tag 5 # 重写列表类的append方法,只允许添加str类型,不然报错 6 def append(self, p_object): 7 # print(p_object) 8 if not isinstance(p_object,str): 9 raise TypeError('%s must be str' %p_object) 10 super(List,self).append(p_object) 11 12 # 添加获取中间值方法,@property可当声明静态变量调用 13 @property 14 def mid(self): 15 mid_index=len(self)//2 16 return self[mid_index] 17 18 # 必须修改tag,才能清空列表 19 def clear(self): 20 if not self.tag: 21 raise PermissionError('not permissive') 22 super().clear() 23 self.tag=False 24 25 # l=List([1,2,3]) 26 # l.append(4) 27 # l.append('aaaaa') 28 # l.append('aaaaa') 29 # print(l) 30 31 # print(l.mid) 32 # l=[1,2,3,4,5,56,6,7,7] 33 # 34 # mid_index=len(l)//2 35 # print(l[mid_index]) 36 37 # l.insert(0,123123123123123) 38 # print(l) 39 40 # l.tag=True 41 # l.clear() 42 # print(l)
2、授权:
授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法。主要针对,python标准库中,不是类属性的工具模块如:函数属性,修改。
范例为:open方法
1 import time 2 class Open: 3 def __init__(self,filepath,mode='r',encoding='utf-8'): 4 self.filepath=filepath 5 self.mode=mode 6 self.encoding=encoding 7 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) 8 9 #写入每行增加日期 10 def write(self,msg): 11 t=time.strftime('%Y-%m-%d %X') 12 self.f.write('%s %s\n' %(t,msg)) 13 14 #其他除了write的方法,都反射会原来工具处理 15 def __getattr__(self, item): 16 # print(item,type(item)) 17 return getattr(self.f,item) 18 obj=Open('a.txt','w+',encoding='utf-8')
范例二:open方法
1 # 我们来加上b模式支持 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 if 'b' in mode: 5 self.file=open(filename,mode) 6 else: 7 self.file=open(filename,mode,encoding=encoding) 8 self.filename=filename 9 self.mode=mode 10 self.encoding=encoding 11 12 # 我们来加上b(二进制)模式支持 13 def write(self,line): 14 if 'b' in self.mode: 15 if not isinstance(line,bytes): 16 raise TypeError('must be bytes') 17 self.file.write(line) 18 19 def __getattr__(self, item): 20 return getattr(self.file,item) 21 22 def __str__(self): 23 if 'b' in self.mode: 24 res="<_io.BufferedReader name='%s'>" %self.filename 25 else: 26 res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) 27 return res 28 f1=FileHandle('b.txt','wb') 29 # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气 30 f1.write('你好啊'.encode('utf-8')) 31 print(f1) 32 f1.close()
练习:使用授权重写list的方法
1 #练习一 2 class List: 3 def __init__(self,seq): 4 self.seq=seq 5 6 def append(self, p_object): 7 ' 派生自己的append加上类型检查,覆盖原有的append' 8 if not isinstance(p_object,int): 9 raise TypeError('must be int') 10 self.seq.append(p_object) 11 12 @property 13 def mid(self): 14 '新增自己的方法' 15 index=len(self.seq)//2 16 return self.seq[index] 17 18 def __getattr__(self, item): 19 return getattr(self.seq,item) 20 21 def __str__(self): 22 return str(self.seq) 23 24 l=List([1,2,3]) 25 print(l) 26 l.append(4) 27 print(l) 28 # l.append('3333333') #报错,必须为int类型 29 30 print(l.mid) 31 32 #基于授权,获得insert方法 33 l.insert(0,-123) 34 print(l) 35 36 37 38 39 40 #练习二 41 class List: 42 def __init__(self,seq,permission=False): 43 self.seq=seq 44 self.permission=permission 45 def clear(self): 46 if not self.permission: 47 raise PermissionError('not allow the operation') 48 self.seq.clear() 49 50 def __getattr__(self, item): 51 return getattr(self.seq,item) 52 53 def __str__(self): 54 return str(self.seq) 55 l=List([1,2,3]) 56 # l.clear() #此时没有权限,抛出异常 57 58 59 l.permission=True 60 print(l) 61 l.clear() 62 print(l) 63 64 #基于授权,获得insert方法 65 l.insert(0,-123) 66 print(l)
五、__getattribute__
__getattriute__与__getattr_意思相反,不管只要使用点调用属性不管存不存在,都会执行。且当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError。
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 class Foo: 5 def __init__(self,x): 6 self.x=x 7 8 def __getattr__(self, item): 9 print('执行的是我') 10 # return self.__dict__[item] 11 def __getattribute__(self, item): 12 print('不管是否存在,我都会执行') 13 raise AttributeError('哈哈') 14 15 f1=Foo(10) 16 f1.x 17 f1.xxxxxx
六、描述符(__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 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Shu Yang Wang 4 # Date: 2017/6/19 5 6 7 #描述符Str 8 class Str: 9 def __get__(self, instance, owner): 10 print('Str调用') 11 def __set__(self, instance, value): 12 print('Str设置...') 13 def __delete__(self, instance): 14 print('Str删除...') 15 16 #描述符Int 17 class Int: 18 def __get__(self, instance, owner): 19 print('Int调用') 20 def __set__(self, instance, value): 21 print('Int设置...') 22 def __delete__(self, instance): 23 print('Int删除...') 24 25 class People: 26 name=Str() 27 age=Int() 28 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 29 self.name=name 30 self.age=age 31 32 #何地?:定义成另外一个类的类属性 33 34 #何时?:且看下列演示 35 36 p1=People('alex',18) 37 ''' 38 Str设置... 39 Int设置... 40 ''' 41 42 #描述符Str的使用 43 p1.name 44 p1.name='egon' 45 del p1.name 46 ''' 47 Str调用 48 Str设置... 49 Str删除... 50 ''' 51 52 #描述符Int的使用 53 p1.age 54 p1.age=18 55 del p1.age 56 ''' 57 Int调用 58 Int设置... 59 Int删除... 60 ''' 61 62 #我们来瞅瞅到底发生了什么 63 print(p1.__dict__) 64 print(People.__dict__) 65 ''' 66 {} 67 {'__module__': '__main__', 'name': <__main__.Str object at 0x0000009B9AB899E8>, 'age': <__main__.Int object at 0x0000009B9AB89A20>, '__init__': <function People.__init__ at 0x0000009B9AB8BBF8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} 68 ''' 69 70 #补充 71 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的 72 print(type(p1).__dict__ == People.__dict__) 73 ''' 74 True 75 True 76 '''
3、描述符的分类:
a.数据描述符: 至少实现一个__get__() 、__set__()
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get')
b.非数据描述符
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
4、使用注意
a.描述符本身应该定义成新式类,被代理的类也应该是新式类
b.必须把描述符定义成这个类的类属性,不能为定义到构造函数中
c.要严格遵循该优先级,优先级由高到底分别:类属性--〉数据描述符--〉实例属性--〉非数据描述符--〉找不到的属性触发__getattr__()
1)类属性--〉数据描述符
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 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 13 self.name=name 14 self.age=age 15 16 17 #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 18 19 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错 20 People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__() 21 22 People.name='egon' #那赋值呢,我去,并没有触发__set__() 23 del People.name #赶紧试试del,我去,也没有触发__delete__() 24 #结论:描述符对类没有作用-------->傻逼到家的结论 25 26 ''' 27 结果: 28 Str调用 29 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 30 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() 31 32 People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__() 33 del People.name #同上 34 '''
2)数据描述符--〉实例属性
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 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 13 self.name=name 14 self.age=age 15 16 17 p1=People('egon',18) 18 19 #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性 20 p1.name='egonnnnnn' 21 p1.name 22 print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 23 del p1.name 24 ''' 25 结果: 26 Str设置... 27 Str设置... 28 Str调用 29 {'age': 18} 30 Str删除... 31 '''
3)实例属性--〉非数据描述符
1 # class Foo: 2 # def __set__(self, instance, value): 3 # print('set') 4 # def __get__(self, instance, owner): 5 # print('get') 6 # class Room: 7 # name=Foo() 8 # def __init__(self,name,width,length): 9 # self.name=name 10 # self.width=width 11 # self.length=length 12 # 13 # 14 # #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级 15 # #对实例的属性操作,触发的都是描述符的 16 # r1=Room('厕所',1,1) 17 # r1.name 18 # r1.name='厨房' 19 ''' 20 set 21 get 22 set 23 ''' 24 25 26 # class Foo: 27 # def __get__(self, instance, owner): 28 # print('get') 29 # class Room: 30 # name=Foo() 31 # def __init__(self,name,width,length): 32 # self.name=name 33 # self.width=width 34 # self.length=length 35 36 37 # #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级 38 # #对实例的属性操作,触发的都是实例自己的 39 # r1=Room('厕所',1,1) 40 # r1.name 41 # r1.name='厨房' 42 ''' 43 没有任何触发 44 '''
4)非数据描述符--〉找不到的属性触发__getattr__()
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 Str: 2 # def __init__(self,name): 3 # self.name=name 4 # def __get__(self, instance, owner): 5 # print('get--->',instance,owner) 6 # return instance.__dict__[self.name] 7 # 8 # def __set__(self, instance, value): 9 # print('set--->',instance,value) 10 # instance.__dict__[self.name]=value 11 # 12 # def __delete__(self, instance): 13 # print('delete--->',instance) 14 # instance.__dict__.pop(self.name) 15 # 16 # 17 # class People: 18 # name=Str('name') 19 # def __init__(self,name,age,salary): 20 # self.name=name 21 # self.age=age 22 # self.salary=salary 23 # 24 # p1=People('egon',18,3231.3) 25 ''' 26 set---> <__main__.People object at 0x0000009A73999A20> egon 27 28 ''' 29 30 #调用 31 # print(p1.__dict__) 32 # p1.name 33 ''' 34 {'name': 'egon', 'age': 18, 'salary': 3231.3} 35 get---> <__main__.People object at 0x0000009A73999A20> <class '__main__.People'> 36 ''' 37 38 #赋值 39 # print(p1.__dict__) 40 # p1.name='egonlin' 41 # print(p1.__dict__) 42 ''' 43 {'name': 'egon', 'age': 18, 'salary': 3231.3} 44 set---> <__main__.People object at 0x0000009A73999A20> egonlin 45 {'name': 'egonlin', 'age': 18, 'salary': 3231.3} 46 ''' 47 48 49 #删除 50 # print(p1.__dict__) 51 # del p1.name 52 # print(p1.__dict__) 53 54 ''' 55 {'name': 'egonlin', 'age': 18, 'salary': 3231.3} 56 delete---> <__main__.People object at 0x0000009A73999A20> 57 {'age': 18, 'salary': 3231.3} 58 '''
添加限制功能:
1 class Typed: 2 def __init__(self,name,expected_type): 3 self.name=name 4 self.expected_type=expected_type 5 def __get__(self, instance, owner): 6 print('get--->',instance,owner) 7 if instance is None: 8 return self 9 return instance.__dict__[self.name] 10 11 def __set__(self, instance, value): 12 print('set--->',instance,value) 13 if not isinstance(value,self.expected_type): 14 raise TypeError('Expected %s' %str(self.expected_type)) 15 instance.__dict__[self.name]=value 16 def __delete__(self, instance): 17 print('delete--->',instance) 18 instance.__dict__.pop(self.name) 19 20 21 class People: 22 name=Typed('name',str) 23 age=Typed('name',int) 24 salary=Typed('name',float) 25 def __init__(self,name,age,salary): 26 self.name=name 27 self.age=age 28 self.salary=salary 29 30 #p1=People(123,18,3333.3) 31 #p1=People('egon','18',3333.3) 32 p1=People('egon',18,3333.3)
通过类装饰器来实现,中级版:
1 class Typed: 2 def __init__(self,name,expected_type): 3 self.name=name 4 self.expected_type=expected_type 5 def __get__(self, instance, owner): 6 print('get--->',instance,owner) 7 if instance is None: 8 return self 9 return instance.__dict__[self.name] 10 11 def __set__(self, instance, value): 12 print('set--->',instance,value) 13 if not isinstance(value,self.expected_type): 14 raise TypeError('Expected %s' %str(self.expected_type)) 15 instance.__dict__[self.name]=value 16 def __delete__(self, instance): 17 print('delete--->',instance) 18 instance.__dict__.pop(self.name) 19 20 def typeassert(**kwargs): 21 def decorate(cls): 22 print('类的装饰器开始运行啦------>',kwargs) 23 for name,expected_type in kwargs.items(): 24 setattr(cls,name,Typed(name,expected_type)) 25 return cls 26 return decorate 27 @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) 28 class People: 29 def __init__(self,name,age,salary): 30 self.name=name 31 self.age=age 32 self.salary=salary 33 34 print(People.__dict__) 35 p1=People('egon',18,3333.3)
ps.类装饰器
a.无参
1 def decorate(cls): 2 print('类的装饰器开始运行啦------>') 3 return cls 4 5 @decorate #无参:People=decorate(People) 6 class People: 7 def __init__(self,name,age,salary): 8 self.name=name 9 self.age=age 10 self.salary=salary 11 12 p1=People('egon',18,3333.3)
b.有参
1 def typeassert(**kwargs): 2 def decorate(cls): 3 print('类的装饰器开始运行啦------>',kwargs) 4 return cls 5 return decorate 6 @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) 7 class People: 8 def __init__(self,name,age,salary): 9 self.name=name 10 self.age=age 11 self.salary=salary 12 13 p1=People('egon',18,3333.3)
6、总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
7、利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)
a.@property用法回顾
1 class Room: 2 def __init__(self,name,width,length): 3 self.name=name 4 self.width=width 5 self.length=length 6 7 @property 8 def area(self): 9 return self.width * self.length 10 11 r1=Room('alex',1,1) 12 print(r1.area)
b.自己实现@property功能
1 class Lazyproperty: 2 def __init__(self,func): 3 self.func=func 4 def __get__(self, instance, owner): 5 print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') 6 if instance is None: 7 return self 8 return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情 9 10 class Room: 11 def __init__(self,name,width,length): 12 self.name=name 13 self.width=width 14 self.length=length 15 16 @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符 17 def area(self): 18 return self.width * self.length 19 20 r1=Room('alex',1,1) 21 print(r1.area)
c.实现延迟计算
1 class Lazyproperty: 2 def __init__(self,func): 3 self.func=func 4 def __get__(self, instance, owner): 5 print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') 6 if instance is None: 7 return self 8 else: 9 print('--->') 10 value=self.func(instance) 11 setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中 12 return value 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 #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符' 21 def area(self): 22 return self.width * self.length 23 24 r1=Room('alex',1,1) 25 print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法 26 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
d.去掉装饰类的返回值,缓存失败
1 #缓存不起来了 2 3 class Lazyproperty: 4 def __init__(self,func): 5 self.func=func 6 def __get__(self, instance, owner): 7 print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') 8 if instance is None: 9 return self 10 else: 11 value=self.func(instance) 12 instance.__dict__[self.func.__name__]=value 13 return value 14 # return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情 15 def __set__(self, instance, value): 16 print('hahahahahah') 17 18 class Room: 19 def __init__(self,name,width,length): 20 self.name=name 21 self.width=width 22 self.length=length 23 24 @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符 25 def area(self): 26 return self.width * self.length 27 28 print(Room.__dict__) 29 r1=Room('alex',1,1) 30 print(r1.area) 31 print(r1.area) 32 print(r1.area) 33 print(r1.area) #缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
8、利用描述符原理完成一个自定制@classmethod
1 class ClassMethod: 2 def __init__(self,func): 3 self.func=func 4 5 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, 6 def feedback(): 7 print('在这里可以加功能啊...') 8 return self.func(owner) 9 return feedback 10 11 class People: 12 name='linhaifeng' 13 @ClassMethod # say_hi=ClassMethod(say_hi) 14 def say_hi(cls): 15 print('你好啊,帅哥 %s' %cls.name) 16 17 People.say_hi() 18 19 p1=People() 20 p1.say_hi() 21 #疑问,类方法如果有参数呢,好说,好说 22 23 class ClassMethod: 24 def __init__(self,func): 25 self.func=func 26 27 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, 28 def feedback(*args,**kwargs): 29 print('在这里可以加功能啊...') 30 return self.func(owner,*args,**kwargs) 31 return feedback 32 33 class People: 34 name='linhaifeng' 35 @ClassMethod # say_hi=ClassMethod(say_hi) 36 def say_hi(cls,msg): 37 print('你好啊,帅哥 %s %s' %(cls.name,msg)) 38 39 People.say_hi('你是那偷心的贼') 40 41 p1=People() 42 p1.say_hi('你是那偷心的贼')
9、利用描述符原理完成一个自定制的@staticmethod
1 class StaticMethod: 2 def __init__(self,func): 3 self.func=func 4 5 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, 6 def feedback(*args,**kwargs): 7 print('在这里可以加功能啊...') 8 return self.func(*args,**kwargs) 9 return feedback 10 11 class People: 12 @StaticMethod# say_hi=StaticMethod(say_hi) 13 def say_hi(x,y,z): 14 print('------>',x,y,z) 15 16 People.say_hi(1,2,3) 17 18 p1=People() 19 p1.say_hi(4,5,6)
七、property:
一个静态属性property本质就是实现了get,set,delete三种方法。
1、用法:
1 class Foo: 2 @property 3 def AAA(self): 4 print('get的时候运行我啊') 5 6 @AAA.setter 7 def AAA(self,value): 8 print('set的时候运行我啊') 9 10 @AAA.deleter 11 def AAA(self): 12 print('delete的时候运行我啊') 13 14 #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter 15 f1=Foo() 16 f1.AAA 17 f1.AAA='aaa' 18 del f1.AAA
2、老式用法:
1 class Foo: 2 def get_AAA(self): 3 print('get的时候运行我啊') 4 5 def set_AAA(self,value): 6 print('set的时候运行我啊') 7 8 def delete_AAA(self): 9 print('delete的时候运行我啊') 10 AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 11 12 f1=Foo() 13 f1.AAA 14 f1.AAA='aaa' 15 del f1.AAA
3、实例:
1 class Goods: 2 3 def __init__(self): 4 # 原价 5 self.original_price = 100 6 # 折扣 7 self.discount = 0.8 8 9 @property 10 def price(self): 11 # 实际价格 = 原价 * 折扣 12 new_price = self.original_price * self.discount 13 return new_price 14 15 @price.setter 16 def price(self, value): 17 self.original_price = value 18 19 @price.deleter 20 def price(self): 21 del self.original_price 22 23 24 obj = Goods() 25 obj.price # 获取商品价格 26 obj.price = 200 # 修改商品原价 27 print(obj.price) 28 del obj.price # 删除商品原价
1 #实现类型检测功能 2 3 #第一关: 4 class People: 5 def __init__(self,name): 6 self.name=name 7 8 @property 9 def name(self): 10 return self.name 11 12 # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 13 14 15 #第二关:修订版 16 17 class People: 18 def __init__(self,name): 19 self.name=name #实例化就触发property 20 21 @property 22 def name(self): 23 # return self.name #无限递归 24 print('get------>') 25 return self.DouNiWan 26 27 @name.setter 28 def name(self,value): 29 print('set------>') 30 self.DouNiWan=value 31 32 @name.deleter 33 def name(self): 34 print('delete------>') 35 del self.DouNiWan 36 37 p1=People('alex') #self.name实际是存放到self.DouNiWan里 38 print(p1.name) 39 print(p1.name) 40 print(p1.name) 41 print(p1.__dict__) 42 43 p1.name='egon' 44 print(p1.__dict__) 45 46 del p1.name 47 print(p1.__dict__) 48 49 50 #第三关:加上类型检查 51 class People: 52 def __init__(self,name): 53 self.name=name #实例化就触发property 54 55 @property 56 def name(self): 57 # return self.name #无限递归 58 print('get------>') 59 return self.DouNiWan 60 61 @name.setter 62 def name(self,value): 63 print('set------>') 64 if not isinstance(value,str): 65 raise TypeError('必须是字符串类型') 66 self.DouNiWan=value 67 68 @name.deleter 69 def name(self): 70 print('delete------>') 71 del self.DouNiWan 72 73 p1=People('alex') #self.name实际是存放到self.DouNiWan里 74 p1.name=1
八、__setitem__,__getitem,__delitem__
在Python中,如果我们想实现创建类似于序列和映射的类,可以通过重写魔法方法__getitem__、__setitem__、__delitem__、__len__方法去模拟。
__getitem__(self,key):返回键对应的值。
__setitem__(self,key,value):设置给定键的值
__delitem__(self,key):删除给定键对应的元素。
__len__():返回元素的数量
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 def __getitem__(self, item): 5 print("getitem") 6 return self.__dict__[item] 7 def __setitem__(self, key, value): 8 print("setitem",key,value) 9 self.__dict__[key]=value 10 def __delitem__(self, key): 11 print('del obj[key]时,我执行') 12 self.__dict__.pop(key) 13 obj=Foo('shuyang') 14 obj.name='shuyang666' 15 print(obj.name) 16 ''' 17 shuyang666 18 ''' 19 print('-----------') 20 obj['name']='shuyang666' 21 ''' 22 setitem name shuyang666 23 ''' 24 print(obj['name']) 25 ''' 26 getitem 27 shuyang666 28 ''' 29 # print(obj.name) 30 del obj['name'] 31 print(obj.__dict__) 32 ''' 33 del obj[key]时,我执行 34 {} 35 '''
九、__str__,__repr__,__format__
1、__str__, __repr__, __format__ 方法
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
1 ormat_dict={ 2 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 3 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 4 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 5 } 6 class School: 7 def __init__(self,name,addr,type): 8 self.name=name 9 self.addr=addr 10 self.type=type 11 12 def __repr__(self): 13 return 'School(%s,%s)' %(self.name,self.addr) 14 def __str__(self): 15 return '(%s,%s)' %(self.name,self.addr) 16 17 def __format__(self, format_spec): 18 # if format_spec 19 if not format_spec or format_spec not in format_dict: 20 format_spec='nat' 21 fmt=format_dict[format_spec] 22 return fmt.format(obj=self) 23 24 s1=School('oldboy1','北京','私立') 25 print('from repr: ',repr(s1)) 26 print('from str: ',str(s1)) 27 print(s1) 28 29 ''' 30 str函数或者print函数--->obj.__str__() 31 repr或者交互式解释器--->obj.__repr__() 32 如果__str__没有被定义,那么就会使用__repr__来代替输出 33 注意:这俩方法的返回值必须是字符串,否则抛出异常 34 ''' 35 print(format(s1,'nat')) 36 print(format(s1,'tna')) 37 print(format(s1,'tan')) 38 print(format(s1,'asfdasdffd'))
2、issubclass和isinstance
1 class A: 2 pass 3 4 class B(A): 5 pass 6 7 print(issubclass(B,A)) #B是A的子类,返回True 8 9 a1=A() 10 print(isinstance(a1,A)) #a1是A的实例
十、__slots__
1、__slots__:
是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)。使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
2、__slots__的应用场景:
字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个。字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
3、注意事项:
__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
4、写法示例:
1 class Foo: 2 __slots__='x' 3 4 5 f1=Foo() 6 f1.x=1 7 f1.y=2#报错 8 print(f1.__slots__) #f1不再有__dict__ 9 10 class Bar: 11 __slots__=['x','y'] 12 13 n=Bar() 14 n.x,n.y=1,2 15 n.z=3#报错
5、具体详解:
http://blog.csdn.net/sxingming/article/details/52892640
十一、__next__和__iter__实现迭代器协议
在__iter__函数中将使__next__中的StopIteration raise的条件归零,则可以循环迭代实例。
1、自增:
1 class Foo: 2 def __init__(self,start,stop): 3 self.num=start 4 self.stop=stop 5 def __iter__(self): 6 return self 7 def __next__(self): 8 if self.num >= self.stop: 9 raise StopIteration # for 不报错 10 n=self.num 11 self.num+=1 12 return n 13 14 f=Foo(1,5) 15 from collections import Iterable,Iterator 16 print(isinstance(f,Iterator)) #是否是一个迭代器 17 18 for i in Foo(1,5): 19 print(i)
2、range步长:
1 class Range: 2 def __init__(self,n,stop,step): 3 self.n=n 4 self.stop=stop 5 self.step=step 6 7 def __next__(self): 8 if self.n >= self.stop: 9 raise StopIteration 10 x=self.n 11 self.n+=self.step 12 return x 13 14 def __iter__(self): 15 return self 16 17 for i in Range(1,7,3): # 18 print(i)
3、斐波那契数列:
1 class Fib: 2 def __init__(self): 3 self._a=0 4 self._b=1 5 6 def __iter__(self): 7 return self 8 9 def __next__(self): 10 self._a,self._b=self._b,self._a + self._b 11 return self._a 12 13 f1=Fib() 14 15 print(f1.__next__()) 16 print(next(f1)) 17 print(next(f1)) 18 19 for i in f1: 20 if i > 100: 21 break 22 print('%s ' %i,end='') 23 24 斐波那契数列
十二、__doc__
1、显示‘ ’ ‘’‘ ’‘’注释信息
1 class Foo: 2 '我是描述信息' 3 pass 4 5 print(Foo.__doc__)
2、该属性无法被继承
1 class Foo: 2 '我是描述信息' 3 pass 4 5 class Bar(Foo): 6 pass 7 print(Bar.__doc__) #该属性无法继承给子类
十三、__module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class C: 5 6 def __init__(self): 7 self.name = ‘SB'
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 3 def __del__(self): 4 print('执行我啦') 5 6 f1=Foo() 7 del f1 8 print('------->') 9 10 #输出结果 11 执行我啦 12 ------->
十五、__enter__和__exit__
1、上下文管理协议__enter__、 __exit__(with open('a.txt') as f:)
即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
1 # class Foo: 2 # def __init__(self, name): 3 # self.name = name 4 # def __enter__(self): 5 # print('__enter__') 6 # return self 7 # 8 # def __exit__(self, exc_type, exc_val, exc_tb): 9 # print('__exit__') 10 # 11 # 12 # with Foo() as x: #x=Foo() 13 # print(x) 14 # print('=>') 15 # print('=>') 16 # print('=>') 17 # print('=>')
2、模拟with...open
1 class Open: 2 def __init__(self,filepath,mode='r',encoding='utf-8'): 3 self.filepath=filepath 4 self.mode=mode 5 self.encoding=encoding 6 7 def __enter__(self): 8 # print('enter') 9 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) 10 return self.f 11 12 def __exit__(self, exc_type, exc_val, exc_tb): 13 # print('exit') 14 self.f.close() 15 return True 16 def __getattr__(self, item): 17 return getattr(self.f,item) 18 19 with Open('a.txt','w') as f: 20 print(f) 21 f.write('aaaaaa') 22 f.wasdf #抛出异常,交给__exit__处理
ps.__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行。
3、用途:
a.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
b.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十六、__call__
1、__call__:
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 class Foo: 2 3 def __init__(self): 4 pass 5 6 def __call__(self, *args, **kwargs): 7 8 print('__call__') 9 10 11 obj = Foo() # 执行 __init__ 12 obj() # 执行 __call__
十七、metaclass(元类)
1、exec(srt, global,local)的内置方法:
参数一:字符串形式的命令
参数二:全局作用域
参数三:局部作用域
exec会在指定的局部作用域内执行字符串内的代码,除非明确地使用global关键字。
1 g={ 2 'x':1, 3 'y':2, 4 'teachers':{'egon','alex','yuanhao','wupeiqi'} 5 } 6 l={'birds':[1,2,3]} 7 8 # exec(""" 9 # def func(): 10 # global x 11 # x='egon' 12 # func() 13 # """,g,l) 14 # print(g) 15 # print(l) 16 17 exec(""" 18 global x 19 x='egon' 20 """,g,l) 21 print(g) 22 print(l)
2、类也是对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,可以:
-
- 把类赋值给一个变量
- 把类作为函数参数进行传递
- 把类作为函数的返回值
- 在运行时动态地创建类
1 # class Foo: 2 # pass 3 4 5 # obj=Foo() 6 # print(type(obj)) 7 # 8 # print(type(Foo)) 9 # def func(): 10 # class Foo: 11 # pass 12 # 13 # print(Foo) 14 # 15 # func()
3、元类:
元类是类的类,是类的模板。元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。
4、创建类的两种方式
a.使用class关键字:
1 # class Chinese(object): 2 # country='China' 3 # def __init__(self,name,age): 4 # self.name=name 5 # self.age=age 6 # def talk(self): 7 # print('%s is talking' %self.name) 8 # print(Chinese) 9 # print(Chinese.__dict__) 10 # print(Chinese.__bases__)
b.手动使用tpye创建类。
准备工作:
创建类主要分为三部分
1 类名
2 类的父类
3 类体
1 #类名 2 class_name='Chinese' 3 #类的父类 4 class_bases=(object,) 5 #类体 6 class_body=""" 7 country='China' 8 def __init__(self,name,age): 9 self.name=name 10 self.age=age 11 def talk(self): 12 print('%s is talking' %self.name) 13 """
第一步:生成名称空间
1 class_dic={} 2 exec(class_body,globals(),class_dic) 3 print(class_dic) 4 #{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
第二步:调用元类type(也可以自定义)来产生类Chinense
1 Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo 2 3 print(Foo) 4 print(type(Foo)) 5 print(isinstance(Foo,type)) 6 ''' 7 <class '__main__.Chinese'> 8 <class 'type'> 9 True 10 '''
ps.这样我们就手动生成了一个可调用的类对象。
type 接收三个参数:
第 1 个参数是字符串 ‘Foo’,表示类名
第 2 个参数是元组 (object, ),表示所有的父类
第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法
补充:若Foo类有继承,即class Foo(Bar):.... 则等同于type('Foo',(Bar,),{})
5、一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
所以类实例化的流程都一样,与三个方法有关:(大前提,任何名字后加括号,都是在调用一个功能,触发一个函数的执行,得到一个返回值)
a.obj=Foo(),会调用产生Foo的类内的__call__方法,Foo()的结果即__call__的结果
b.调用__call__方法的过程中,先调用Foo.__new__,得到obj,即实例化的对象,但是还没有初始化
c.调用__call__方法的过程中,如果Foo.__new__()返回了obj,再调用Foo.__init__,将obj传入,进行初始化(否则不调用Foo.__init__)
ps:
__new__更像是其他语言中的构造函数,必须有返回值,返回值就实例化的对象
__init__只是初始化函数,必须没有返回值,仅仅只是初始化功能,并不能new创建对象
a.在我们自定义的元类内,__new__方法在产生obj时用type.__new__(cls,*args,**kwargs),用object.__new__(cls)抛出异常:TypeError: object.__new__(Mymeta) is not safe, use type.__new__()
b.在我们自定义的类内,__new__方法在产生obj时用object.__new__(self)
6、元类控制创建类:
1 class Mymeta(type): 2 def __init__(self): 3 print('__init__') 4 5 def __new__(cls, *args, **kwargs): 6 print('__new__') 7 8 def __call__(self, *args, **kwargs): 9 print('__call__') 10 11 class Foo(metaclass=Mymeta): 12 pass 13 14 print(Foo) 15 ''' 16 打印结果: 17 __new__ 18 None 19 ''' 20 21 ''' 22 分析Foo的产生过程,即Foo=Mymeta(),会触发产生Mymeta的类内的__call__,即元类的__call__: 23 Mymeta加括号,会触发父类的__call__,即type.__call__ 24 在type.__call__里会调用Foo.__new__ 25 而Foo.__new__内只是打印操作,没有返回值,因而Mymeta的结果为None,即Foo=None 26 '''
1 class Mymeta(type): 2 def __init__(self,name,bases,dic): 3 for key,value in dic.items(): 4 if key.startswith('__'): 5 continue 6 if not callable(value): 7 continue 8 if not value.__doc__: 9 raise TypeError('%s 必须有文档注释' %key) 10 type.__init__(self,name,bases,dic) 11 12 def __new__(cls, *args, **kwargs): 13 # print('__new__') 14 obj=type.__new__(cls,*args,**kwargs) 15 return obj 16 17 def __call__(self, *args, **kwargs): 18 # print('__call__') 19 pass 20 21 # Foo=Mymeta() 22 class Foo(metaclass=Mymeta): 23 def f1(self): 24 'from f1' 25 pass 26 27 def f2(self): 28 pass 29 30 ''' 31 抛出异常 32 TypeError: f2 必须有文档注释 33 '''
7、元类控制类创建对象
1 class Mymeta(type): 2 def __call__(self, *args, **kwargs): 3 #self=<class '__main__.Foo'> 4 #args=('egon',) 5 #kwargs={'age':18} 6 obj=self.__new__(self) #创建对象:Foo.__new__(Foo) 7 self.__init__(obj,*args,**kwargs) #初始化对象:Foo.__init__(obj,'egon',age=18) 8 return obj #一定不要忘记return 9 class Foo(metaclass=Mymeta): 10 def __init__(self,name,age): 11 self.name=name 12 self.age=age 13 14 def __new__(cls, *args, **kwargs): 15 return object.__new__(cls,*args,**kwargs) 16 17 obj=Foo('egon',age=18) #触发Mymeta.__call__ 18 19 print(obj.__dict__)
ps.单例模式:比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存
1 #单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存 2 class Mysql: 3 __instance=None 4 def __init__(self,host='127.0.0.1',port='3306'): 5 self.host=host 6 self.port=port 7 8 @classmethod 9 def singleton(cls,*args,**kwargs): 10 if not cls.__instance: 11 cls.__instance=cls(*args,**kwargs) 12 return cls.__instance 13 14 15 obj1=Mysql() 16 obj2=Mysql() 17 print(obj1 is obj2) #False 18 19 obj3=Mysql.singleton() 20 obj4=Mysql.singleton() 21 print(obj3 is obj4) #True
1 #单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存 2 class Mymeta(type): 3 def __init__(self,name,bases,dic): #定义类Mysql时就触发 4 self.__instance=None 5 super().__init__(name,bases,dic) 6 7 def __call__(self, *args, **kwargs): #Mysql(...)时触发 8 9 if not self.__instance: 10 self.__instance=object.__new__(self) #产生对象 11 self.__init__(self.__instance,*args,**kwargs) #初始化对象 12 #上述两步可以合成下面一步 13 # self.__instance=super().__call__(*args,**kwargs) 14 15 return self.__instance 16 class Mysql(metaclass=Mymeta): 17 def __init__(self,host='127.0.0.1',port='3306'): 18 self.host=host 19 self.port=port 20 21 22 obj1=Mysql() 23 obj2=Mysql() 24 25 print(obj1 is obj2)
8、自制元类:
1 class Mytype(type): 2 def __init__(self,class_name,bases=None,dict=None): 3 print("Mytype init--->") 4 print(class_name,type(class_name)) 5 print(bases) 6 print(dict) 7 8 def __call__(self, *args, **kwargs): 9 print('Mytype call---->',self,args,kwargs) 10 obj=self.__new__(self) 11 self.__init__(obj,*args,**kwargs) 12 return obj 13 14 class Foo(object,metaclass=Mytype):#in python3 15 #__metaclass__ = MyType #in python2 16 x=1111111111 17 def __init__(self,name): 18 self.name=name 19 20 def __new__(cls, *args, **kwargs): 21 return super().__new__(cls) 22 # return object.__new__(cls) #同上 23 24 25 f1=Foo('name') 26 print(f1.__dict__)
1 #纯净版 2 class Mytype(type): 3 def __init__(self,what,bases=None,dict=None): 4 print('mytype init') 5 6 def __call__(self, *args, **kwargs): 7 obj=self.__new__(self) 8 self.__init__(obj,*args,**kwargs) 9 return obj 10 11 class Foo(object,metaclass=Mytype): 12 x=1111111111 13 14 def __init__(self,name): 15 self.name=name 16 17 def __new__(cls, *args, **kwargs): 18 return super().__new__(cls) 19 20 f1=Foo('egon') 21 22 print(f1.__dict__)
1 #精简版 2 class Mytype(type): 3 def __init__(self,what,bases=None,dict=None): 4 print(what,bases,dict) 5 6 def __call__(self, *args, **kwargs): 7 print('--->') 8 obj=object.__new__(self) 9 self.__init__(obj,*args,**kwargs) 10 return obj 11 class Room(metaclass=Mytype): 12 def __init__(self,name): 13 self.name=name 14 15 r1=Room('alex') 16 print(r1.__dict__)
9、元类总结:
1 #元类总结 2 class Mymeta(type): 3 def __init__(self,name,bases,dic): 4 print('===>Mymeta.__init__') 5 6 7 def __new__(cls, *args, **kwargs): 8 print('===>Mymeta.__new__') 9 return type.__new__(cls,*args,**kwargs) 10 11 def __call__(self, *args, **kwargs): 12 print('aaa') 13 obj=self.__new__(self) 14 self.__init__(self,*args,**kwargs) 15 return obj 16 17 class Foo(object,metaclass=Mymeta): 18 def __init__(self,name): 19 self.name=name 20 def __new__(cls, *args, **kwargs): 21 return object.__new__(cls) 22 23 ''' 24 需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__ 25 26 而爹.__call__一般做两件事: 27 1.调用name.__new__方法并返回一个对象 28 2.进而调用name.__init__方法对儿子name进行初始化 29 ''' 30 31 ''' 32 class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行 33 Foo=Mymeta('foo',(...),{...}) 34 因此我们可以看到,只定义class就会有如下执行效果 35 ===>Mymeta.__new__ 36 ===>Mymeta.__init__ 37 实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作, 38 遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法 39 于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化 40 ''' 41 42 ''' 43 obj=Foo('egon') 44 的原理同上 45 ''' 46 47 ''' 48 总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了 49 1.谁后面跟括号,就从谁的爹中找__call__方法执行 50 type->Mymeta->Foo->obj 51 Mymeta()触发type.__call__ 52 Foo()触发Mymeta.__call__ 53 obj()触发Foo.__call__ 54 2.__call__内按先后顺序依次调用儿子的__new__和__init__方法 55 '''
10、元类示例:
a.在元类中控制把自定义类的数据属性都变成大写
1 class Mymetaclass(type): 2 def __new__(cls,name,bases,attrs): 3 update_attrs={} 4 for k,v in attrs.items(): 5 if not callable(v) and not k.startswith('__'): 6 update_attrs[k.upper()]=v 7 else: 8 update_attrs[k]=v 9 return type.__new__(cls,name,bases,update_attrs) 10 11 class Chinese(metaclass=Mymetaclass): 12 country='China' 13 tag='Legend of the Dragon' #龙的传人 14 def walk(self): 15 print('%s is walking' %self.name) 16 17 18 print(Chinese.__dict__) 19 ''' 20 {'__module__': '__main__', 21 'COUNTRY': 'China', 22 'TAG': 'Legend of the Dragon', 23 'walk': <function Chinese.walk at 0x0000000001E7B950>, 24 '__dict__': <attribute '__dict__' of 'Chinese' objects>, 25 '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, 26 '__doc__': None} 27 '''
b.在元类中控制自定义的类无需__init__方法
1.元类帮其完成创建对象,以及初始化操作;
2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument for key function;
3.key作为用户自定义类产生对象的属性,且所有属性变成大写
1 class Mymetaclass(type): 2 # def __new__(cls,name,bases,attrs): 3 # update_attrs={} 4 # for k,v in attrs.items(): 5 # if not callable(v) and not k.startswith('__'): 6 # update_attrs[k.upper()]=v 7 # else: 8 # update_attrs[k]=v 9 # return type.__new__(cls,name,bases,update_attrs) 10 11 def __call__(self, *args, **kwargs): 12 if args: 13 raise TypeError('must use keyword argument for key function') 14 obj = object.__new__(self) #创建对象,self为类Foo 15 16 for k,v in kwargs.items(): 17 obj.__dict__[k.upper()]=v 18 return obj 19 20 class Chinese(metaclass=Mymetaclass): 21 country='China' 22 tag='Legend of the Dragon' #龙的传人 23 def walk(self): 24 print('%s is walking' %self.name) 25 26 27 p=Chinese(name='egon',age=18,sex='male') 28 print(p.__dict__)