Python学习之面向对象进阶
https://www.cnblogs.com/linhaifeng/articles/6204014.html#_label2
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象):

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

1 #根据字符串获取obj对象里的对应的方法的内存地址 2 def getattr(object, name, default=None): # known special case of getattr 3 """ 4 getattr(object, name[, default]) -> value 5 6 Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. 7 When a default argument is given, it is returned when the attribute doesn't 8 exist; without it, an exception is raised in that case. 9 """ 10 pass 11 12 getattr(object, name, default=None)

1 #给对象添加新的属性。"x.y = v" :obj是对象,y字符串,v方法,属性 2 def setattr(x, y, v): # real signature unknown; restored from __doc__ 3 """ 4 Sets the named attribute on the given object to the specified value. 5 6 setattr(x, 'y', v) is equivalent to ``x.y = v'' 7 """ 8 pass

1 def delattr(x, y): # real signature unknown; restored from __doc__ 2 """ 3 Deletes the named attribute from the given object. 4 5 delattr(x, 'y') is equivalent to ``del x.y'' 6 """ 7 pass

1 class BlackMedium: 2 feture='Ugly' 3 def __init__(self,name,addr): 4 self.name=name 5 self.addr=addr 6 7 def sell_hourse(self): 8 print('【%s】 正在卖房子,sb才买呢' %self.name) 9 10 def rent_hourse(self): 11 print('【%s】 正在租房子,sb才租呢' % self.name) 12 b1=BlackMedium('万成置地','天露园') 13 #检测是否含有某属性 14 print(hasattr(b1,'name')) # True 15 print(hasattr(b1,'sell_hourse')) #True 16 print(hasattr(b1,'selasdfasdfsadfasdfasdfasdfasdl_hourse')) #Flase 17 18 #获取属性 19 print(getattr(b1,'name')) #万成置地 20 print(getattr(b1,'rent_hourse')) #<bound method BlackMedium.rent_hourse of <__main__.BlackMedium object at 0x0000005962836C50>> 21 func=getattr(b1,'rent_hourse') 22 func() #【万成置地】 正在租房子,sb才租呢 23 #print(getattr(b1,'rent_hourseasdfsa')) #没有则报错 24 print(getattr(b1,'rent_hourseasdfsa','没有这个属性')) #没有第三个参数则报错 25 26 #设置属性 27 setattr(b1,'sb',True) # 等同于b1.sb=True 28 setattr(b1,'func',lambda x:x+1) 29 print(b1.__dict__) #{'name': '万成置地', 'addr': '天露园', 'sb': True, 'func': <function <lambda> at 0x0000008481133E18>} 30 #删除属性 31 delattr(b1,'sb') #等同于del b1.sb 32 print(b1.__dict__) #{'name': '万成置地', 'addr': '天露园', 'func': <function <lambda> at 0x000000B5416A3E18>}
为什么用反射之反射的好处
好处一:实现可插拔机制
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

1 #ftp_client文件下内容: 2 class FtpClient: 3 'ftp客户端,但是还么有实现具体的功能' 4 def __init__(self,addr): 5 print('正在连接服务器[%s]' %addr) 6 self.addr=addr 7 def put(self): 8 print('正在上传文件') 9 10 ****************************** 11 #使用者文件下: 12 from ftp_client import FtpClient 13 14 f1=FtpClient('1.1.1.1') 15 # f1.put() 16 17 if hasattr(f1,'put'): 18 func_get=getattr(f1,'put') 19 func_get() 20 else: 21 print('其他的逻辑')
好处二:动态导入模块(基于反射当前模块成员)
module_t=__import__('m1.t') print(module_t) module_t.t.test1() from m1.t import * from m1.t import test1,_test2 test1() _test2() import importlib m=importlib.import_module('m1.t') print(m) m.test1() m._test2() 输出: <module 'm1' (namespace)> test1 test1 test2 <module 'm1.t' from 'F:\\python视频\\python全栈s3 day26\\day26课上代码\\m1\\t.py'> test1 test2
双下划线开头的attr方法:

1 #调用属性不存在时执行__getattr__: 2 class Foo: 3 x=1 4 def __init__(self,y): 5 self.y=y 6 7 def __getattr__(self, item): 8 print('执行__getattr__') 9 10 f1=Foo(10) 11 print(f1.y) #10 12 print(getattr(f1,'y')) #10 #len(str)--->str.__len__() 13 f1.ssssss #执行__getattr__ 14 15 #删除属性时会触发_delattr__: 16 class Foo: 17 x=1 18 def __init__(self,y): 19 self.y=y 20 21 def __delattr__(self, item): 22 print('删除操作__delattr__') 23 24 f1=Foo(10) 25 del f1.y #删除操作__delattr__ 26 del f1.x #删除操作__delattr__ 27 28 # 设置属性的时候会触发——setattr———: 29 class Foo: 30 x=1 31 def __init__(self,y): 32 self.y=y 33 34 def __setattr__(self, key, value): 35 print('__setattr__执行') 36 # self.key=value #这就无限递归了 37 self.__dict__[key]=value 38 f1=Foo(10) #__setattr__执行 39 print(f1.__dict__) #{'y': 10} 40 f1.z=2 #__setattr__执行 41 print(f1.__dict__) #{'y': 10, 'z': 2}
class Foo: def __init__(self,name): self.name=name def __getattr__(self, item): print('你找的属性【%s】不存在' %item) def __setattr__(self, k,v): print('执行setattr',k,v) if type(v) is str: print('开始设置') # self.k=v #触发__setattr__ self.__dict__[k]=v.upper() else: print('必须是字符串类型') def __delattr__(self, item): print('不允许删除属性【%s】' %item) print('执行delattr',item) # del self.item # self.__dict__.pop(item) f1=Foo('alex') #执行setattr name alex 开始设置 f1.age=18 #触发__setattr__ 执行setattr age 18 必须是字符串类型 print(f1.__dict__) #{'name': 'ALEX'} print(f1.name) #ALEX print(f1.age) #你找的属性【age】不存在 None print(f1.gender) #你找的属性【gender】不存在 None print(f1.slary) #你找的属性【slary】不存在 None print(f1.__dict__) #{'name': 'ALEX'} del f1.name #不允许删除属性【name】 print(f1.__dict__) #{'name': 'ALEX'}
二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

1 class List(list): 2 def append(self, p_object): 3 if type(p_object) is str: 4 # self.append(p_object) 5 super().append(p_object) #super() 函数是用于调用父类(超类)的一个方法。 6 else: 7 print('只能添加字符串类型') 8 def show_midlle(self): 9 mid_index=int(len(self)/2) 10 return self[mid_index] 11 12 l2=list('he llo') 13 print(l2,type(l2)) #['h', 'e', ' ', 'l', 'l', 'o'] <class 'list'> 14 15 l1=List('hello') 16 print(l1,type(l1)) #['h', 'e', 'l', 'l', 'o'] <class '__main__.List'> 17 print(l1.show_midlle()) # l 18 l1.append(11111) #只能添加字符串类型 19 l1.append('Ss') 20 print(l1) #['h', 'e', 'l', 'l', 'o', 'Ss']
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法

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 print(f1.file) 20 print(f1.__dict__) 21 print('==>',f1.read) #触发__getattr__ 22 print(f1.write) 23 f1.write('1111111111111111\n') 24 f1.write('cpu负载过高\n') 25 f1.write('内存剩余不足\n') 26 f1.write('硬盘剩余不足\n') 27 f1.seek(0) 28 print('--->',f1.read())

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 def write(self,line): 6 t=time.strftime('%Y-%m-%d %T') 7 self.file.write('%s %s' %(t,line)) 8 9 def __getattr__(self, item): 10 return getattr(self.file,item) 11 12 f1=FileHandle('b.txt','w+') 13 f1.write('你好啊') 14 f1.seek(0) #不存在的属性seek触发__getattr__ 15 print(f1.read()) 16 f1.close() 17 18 示例
__getattribute__

1 #不存在的属性访问,触发__getattr__ : 2 class Foo: 3 def __init__(self,x): 4 self.x=x 5 6 def __getattr__(self, item): 7 print('执行的是我') 8 f1=Foo(10) 9 print(f1.x) #10 10 f1.xxxxxx # 执行的是我 11 12 #不管属性是否存在都会执行__getattribute__ 13 class Foo: 14 def __init__(self,x): 15 self.x=x 16 17 def __getattribute__(self, item): 18 print('不管是否存在,我都会执行') 19 20 f1=Foo(10) 21 f1.x #不管是否存在,我都会执行 22 f1.xxxxxx #不管是否存在,我都会执行 23 24 #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__, 25 # 除非__getattribute__在执行过程中抛出异常AttributeError,会继续执行__getattr__ 26 class Foo: 27 def __init__(self,x): 28 self.x=x 29 def __getattr__(self, item): 30 print('执行的是getattr') 31 # return self.__dict__[item] 32 def __getattribute__(self, item): 33 print('执行的是getattribute') 34 # raise AttributeError('抛出异常了') 35 f1=Foo(10) 36 f1.x #执行的是getattribute 37 f1.xxxxxx #执行的是getattribute
__setitem__,__getitem,__delitem__

1 class Foo: 2 def __getitem__(self, item): 3 print('getitem',item) 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 f1=Foo() 15 print(f1.__dict__) #{} 16 # f1.name='egon' #---->setattr-------->f1.__dict__['name']='egon' 17 f1['name']='egon'#--->setitem--------->f1.__dict__['name']='egon' 18 f1['age']=18 #setitem 19 20 print('===>',f1.__dict__) #===> {'name': 'egon', 'age': 18} 21 22 del f1['name'] #delitem 23 print(f1.__dict__) #{'age': 18} 24 print(f1['age']) #18
.的方式操作属性跟attr系列相关 ; []的方式操作属性跟item系列相关
__str__,__repr__

1 l=list('hello') 2 print(l) #['h', 'e', 'l', 'l', 'o'] 3 file=open('test.txt','w') 4 print(file) #<_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'> 5 6 #自订制str方法 7 class Foo: 8 def __init__(self,name,age): 9 self.name=name 10 self.age=age 11 def __str__(self): #str可自己控制return中打印的信息 12 return '名字是%s 年龄是%s' %(self.name,self.age) 13 #以下三种调用均可 14 f1=Foo('egon',18) 15 print(f1) #str(f1)--->f1.__str__() 名字是egon 年龄是18 16 17 x=str(f1) 18 print(x) #名字是egon 年龄是18 19 20 y=f1.__str__() 21 print(y) #名字是egon 年龄是18 22 23 #repr 24 class Foo: 25 def __init__(self,name,age): 26 self.name=name 27 self.age=age 28 # def __str__(self): 29 # return '是str' 30 def __repr__(self): 31 return '名字是%s 年龄是%s' %(self.name,self.age) 32 f1=Foo('egon',19) 33 print(f1) # repr(f1)---->f1.__repr__() #名字是egon 年龄是19 34 35 #str与repr共存时: 36 class Foo: 37 def __init__(self,name,age): 38 self.name=name 39 self.age=age 40 def __str__(self): 41 return '是str' 42 def __repr__(self): 43 return '名字是%s 年龄是%s' %(self.name,self.age) 44 45 f1=Foo('egon',19) 46 print(f1) #str(f1)---》f1.__str__()------>f1.__repr__() #是str
{str函数或者print函数--->obj.__str__()repr或者交互式解释器--->obj.__repr__()如果__str__没有被定义,那么就会使用__repr__来代替输出注意:这俩方法的返回值必须是字符串,否则抛出异常}
自定制格式化字符串__format__

1 x='{0}{0}{0}'.format('dog') 2 print(x) #dogdogdog 3 ********************************* 4 class Date: 5 def __init__(self,year,mon,day): 6 self.year=year 7 self.mon=mon 8 self.day=day 9 d1=Date(2018,8,7) 10 11 x='{0.year}{0.mon}{0.day}'.format(d1) 12 y='{0.year}:{0.mon}:{0.day}'.format(d1) 13 z='{0.mon}-{0.day}-{0.year}'.format(d1) 14 print(x) #201887 15 print(y) #2018:8:7 16 print(z) #8-7-2018 17 18 ************************************** 19 #自定制格式化方式format 20 format_dic={ #将想要的格式事先定义好放入字典中之后通过传key值调用 21 'ymd':'{0.year}{0.mon}{0.day}', 22 'm-d-y':'{0.mon}-{0.day}-{0.year}', 23 'y:m:d':'{0.year}:{0.mon}:{0.day}' 24 } 25 class Date: 26 def __init__(self,year,mon,day): 27 self.year=year 28 self.mon=mon 29 self.day=day 30 def __format__(self, format_spec): 31 # print('我执行啦') 32 print('--->',format_spec) 33 if not format_spec or format_spec not in format_dic: #如果format_spec是空或者不是format_dic中的key格式,给出一个默认格式ymd 34 format_spec='ymd' 35 fm=format_dic[format_spec] 36 return fm.format(self) 37 d1=Date(2018,8,7) 38 format(d1) #d1.__format__() : ---> 39 print(format(d1)) #---> 201887 40 print(format(d1,'ymd')) #---> ymd 201887 41 print(format(d1,'y:m:d')) #---> y:m:d 2018:8:7 42 print(format(d1,'m-d-y')) #---> m-d-y 8-7-2018 43 print(format(d1,'m-d:y')) #---> m-d:y 201887 44 print('=======>',format(d1,'fasd')) #---> fasd =======> 201887
__slots__

1 ''' 2 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 3 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 4 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 5 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 6 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 7 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 8 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 9 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 10 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。 11 12 ''' 13 class Foo: 14 __slots__=['name','age'] #{'name':None,'age':None} 15 16 f1=Foo() 17 f1.name='egon' 18 print(f1.name) #egon 19 20 f1.age=18 #--->setattr----->f1.__dict__['age']=18 21 22 # print(f1.__dict__) #'Foo' object has no attribute '__dict__' 23 print(Foo.__slots__) #['name', 'age'] 24 print(f1.__slots__) #['name', 'age'] 25 f1.name='egon' 26 f1.age=17 27 print(f1.name) #egon 28 print(f1.age) #17 29 #f1.gender='male' #'Foo' object has no attribute 'gender' 30 31 f2=Foo() 32 print(f2.__slots__) #['name', 'age'] 33 f2.name='alex' 34 f2.age=18 35 print(f2.name) #alex 36 print(f2.age) #18
__doc__

1 class Foo: 2 '我是描述信息' 3 pass 4 5 print(Foo.__doc__) #我是描述信息 6 7 #该属性无法继承给子类 8 class Foo: 9 '我是描述信息' 10 pass 11 class Bar(Foo): 12 pass 13 print(Bar.__doc__) #None
__module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么

1 #lib文件夹下的aa.py: 2 class C: 3 4 def __init__(self): 5 self.name = 'Su' 6 7 8 #其他文件内容: 9 from lib.aa import C 10 obj = C() 11 print (obj.__module__) # 输出 lib.aa,即:输出模块 12 print (obj.__class__) # 输出 <class 'lib.aa.C'>,即:输出类
析构方法__del__

1 析构方法,当对象在内存中被释放时,自动触发执行。 2 注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__ 3 典型的应用场景: 4 创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中 5 当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源 6 class Foo: 7 def __init__(self,name): 8 self.name=name 9 def __del__(self): 10 print('我执行啦') 11 12 f1=Foo('alex') 13 14 del f1 #删除实例会触发__del__ : 我执行啦 --------> 15 16 del f1.name #删除实例的属性不会触发__del__: --------> 我执行啦 17 print('-------->') 18 19 #程序运行完毕会自动回收内存,触发__del__
__call__

1 对象后面加括号,触发执行。 2 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() 3 4 class Foo: 5 6 def __init__(self): 7 pass 8 9 def __call__(self, *args, **kwargs): 10 11 print('__call__') 12 13 14 obj = Foo() # 执行 __init__ 15 obj() # 执行 __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 == 13: 8 raise StopIteration('终止了') 9 self.n+=1 10 return self.n 11 12 f1=Foo(10) 13 print(f1.__next__()) #11 14 print(f1.__next__()) #12 15 print(f1.__next__()) #13 16 print(f1.__next__()) #StopIteration: 终止了 17 18 for i in f1: # 相当于执行obj=iter(f1)------------>f1.__iter__() 注:不加iter next属性时执行此for循环会报错 19 print(i) #执行obj.__next_() : 11 12 13

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 if self._a > 100: 9 raise StopIteration('终止了') 10 self._a,self._b=self._b,self._a + self._b 11 return self._a 12 f1=Fib() 13 print(next(f1)) 14 print(next(f1)) 15 print(next(f1)) 16 print(next(f1)) 17 print(next(f1)) 18 print('==================================') 19 for i in f1: 20 print(i) 21 22 输出: 23 1 24 2 25 3 26 5 27 8 28 ================================== 29 13 30 21 31 34 32 55 33 89 34 144
描述符(__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

1 class Foo: 2 def __get__(self, instance, owner): 3 print('===>get方法') 4 def __set__(self, instance, value): 5 print('===>set方法',instance,value) 6 instance.__dict__['x']=value #b1.__dict__ 7 def __delete__(self, instance): 8 print('===>delete方法') 9 10 class Bar: 11 x=Foo() #在何地? 12 def __init__(self,n): 13 self.x=n #b1.x=10 14 b1=Bar(10) #===>set方法 <__main__.Bar object at 0x000000DEF9432CC0> 10 15 print(b1.__dict__) #{'x': 10} 16 b1.x=11111 #===>set方法 <__main__.Bar object at 0x0000002FD2552CC0> 11111 17 print(b1.__dict__) #{'x': 11111} 18 19 b1.y=1111111 20 print(b1.__dict__) #{'x': 11111, 'y': 1111111} 21 print(Bar.__dict__) #{'__module__': '__main__', 'x': <__main__.Foo object at 0x00000006582B2BE0>, '__init__': <function Bar.__init__ at 0x00000006582B40D0>, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None} 22 #在何时? 23 # b1=Bar() 24 b1.x #===>get方法 25 b1.x=1 #===>set方法 <__main__.Bar object at 0x0000000F78B12CC0> 1 26 27 del b1.x #===>delete方法 28 print(b1.x) #===>get方法 None
3 描述符分两种

1 一 数据描述符:至少实现了__get__()和__set__() 2 class Foo: 3 def __set__(self, instance, value): 4 print('set') 5 def __get__(self, instance, owner): 6 print('get') 7 8 二 非数据描述符:没有实现__set__() 9 class Foo: 10 def __get__(self, instance, owner): 11 print('get')
4 注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成另外一个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

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 class People: 10 name=Str() 11 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 12 self.name=name 13 self.age=age 14 #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 15 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错 16 People.name #Str调用 调用类属性name,本质就是在调用描述符Str,触发了__get__() 17 18 People.name='egon' #赋值并没有触发__set__() 19 del People.name #del也没有触发__delete__() 20 #结论:描述符对类没有作用 21 ''' 22 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 23 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() 24 25 People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__() 26 del People.name #同上 27 '''

1 class Str: 2 def __get__(self, instance, owner): 3 print('Str调用') 4 def __set__(self, instance, value): 5 print('Str设置...') 6 def __delete__(self, instance): 7 print('Str删除...') 8 9 class People: 10 name=Str() 11 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 12 self.name=name 13 self.age=age 14 p1=People('egon',18) #Str设置... 15 16 #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性 17 p1.name='egonnnnnn' #Str设置... 18 p1.name #Str调用 19 print(p1.__dict__)#{'age': 18} 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 20 del p1.name #Str删除...

1 class Foo: 2 def func(self): 3 print('哈哈哈') 4 f1=Foo() 5 f1.func() #哈哈哈 调用类的方法,也可以说是调用非数据描述符 6 #函数是一个非数据描述符对象(一切皆对象么) 7 print(dir(Foo.func)) #['__annotations__', '__call__', '__class__'...'__subclasshook__'] 8 print(hasattr(Foo.func,'__set__')) #False 9 print(hasattr(Foo.func,'__get__')) #True 10 print(hasattr(Foo.func,'__delete__')) #Flase 11 #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了 12 #描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么 13 #函数就是一个由非描述符类实例化得到的对象 14 #没错,字符串也一样 15 f1.func='这是实例属性啊' 16 print(f1.func) #这是实例属性啊 17 18 del f1.func #删掉了非数据 19 f1.func() #哈哈哈

1 class Foo: 2 def func(self): 3 print('哈哈哈') 4 5 def __getattr__(self, item): 6 print('找不到了当然是来找我啦',item) 7 f1=Foo() 8 9 f1.xxx #找不到了当然是来找我啦 xxx

1 #输入的姓名年龄限制类型且不符合给予提示 2 class Typed: 3 def __init__(self,key,expected_type): 4 self.key=key 5 self.expected_type=expected_type 6 def __get__(self, instance, owner): 7 print('get方法') 8 return instance.__dict__[self.key] 9 def __set__(self, instance, value): 10 print('set方法') 11 if not isinstance(value,self.expected_type): 12 raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type)) 13 instance.__dict__[self.key]=value 14 def __delete__(self, instance): 15 print('delete方法') 16 instance.__dict__.pop(self.key) 17 18 class People: 19 name=Typed('name',str) #t1.__set__() self.__set__() 20 age=Typed('age',int) #t1.__set__() self.__set__() 21 def __init__(self,name,age,salary): 22 self.name=name 23 self.age=age 24 self.salary=salary 25 26 p1=People('alex','13',13.3) #TypeError: age 传入的类型不是<class 'int'> 27 p1=People(213,13,13.3) #TypeError: name 传入的类型不是<class 'str'> 28 29 p1=People('alex',13,13.3) #set方法 set方法 30 print(p1.__dict__) #{'name': 'alex', 'age': 13, 'salary': 13.3}
6 描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

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) #1

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) 22 输出: 23 这是我们自己定制的静态属性,r1.area实际是要执行r1.area() 24 1

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) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算 27 输出: 28 这是我们自己定制的静态属性,r1.area实际是要执行r1.area() 29 ---> 30 1 31 1
property
一个静态属性property本质就是实现了get,set,delete三种方法

1 class Foo: 2 @property 3 def AAA(self): 4 print('get的时候运行我啊') 5 @AAA.setter 6 def AAA(self,value): 7 print('set的时候运行我啊') 8 @AAA.deleter 9 def AAA(self): 10 print('delete的时候运行我啊') 11 #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter 12 f1=Foo() 13 14 f1.AAA 15 f1.AAA='aaa' 16 del f1.AAA 17 输出: 18 get的时候运行我啊 19 set的时候运行我啊 20 delete的时候运行我啊

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 16 输出: 17 get的时候运行我啊 18 set的时候运行我啊 19 delete的时候运行我啊

1 class Goods: 2 def __init__(self): 3 # 原价 4 self.original_price = 100 5 # 折扣 6 self.discount = 0.8 7 @property 8 def price(self): 9 # 实际价格 = 原价 * 折扣 10 new_price = self.original_price * self.discount 11 return new_price 12 @price.setter 13 def price(self, value): 14 self.original_price = value 15 @price.deleter 16 def price(self): 17 del self.original_price 18 19 obj = Goods() 20 obj.price # 获取商品价格 21 obj.price = 200 # 修改商品原价 22 print(obj.price) 23 del obj.price # 删除商品原价 24 输出: 25 160.0
上下文管理协议__enter__和__exit__
我们知道在操作文件对象的时候可以这么写
1 with open('a.txt') as f: 2 '代码块'
上述叫做上下文管理协议,即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 def __exit__(self, exc_type, exc_val, exc_tb): 8 print('执行exit') 9 return True 10 with Foo('a.txt') as f: #with Foo触发enter,enter返回的值self会赋值给f,然后执行下面的代码快执行完触发exit 11 print(f) 12 print(f.name) 13 print('-----------------') 14 print('-----------------') 15 print('-----------------') 16 print('-----------------') 17 print('0000000000000') 18 19 输出: 20 执行enter 21 <__main__.Foo object at 0x000000C45A8C2CC0> 22 a.txt 23 ----------------- 24 ----------------- 25 ----------------- 26 ----------------- 27 执行exit 28 0000000000000

1 class Foo: 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 #__exit__的返回值为True,代表吞掉了异常 13 with Foo('a.txt') as f: 14 print(f) 15 print(asdfsaasdfasdfasdf) #抛出异常触发__exit__ 后续代码不会运行 16 print(f.name) 17 print('-----------------') 18 print('-----------------') 19 print('-----------------') 20 print('-----------------') 21 print('0000000000000') 22 输出: 23 执行enter 24 <__main__.Foo object at 0x000000B903332C88> 25 执行exit 26 <class 'NameError'> 27 name 'asdfsaasdfasdfasdf' is not defined 28 <traceback object at 0x000000B90334D248> 29 0000000000000

1 with obj as f: 2 '代码块' 3 4 1.with obj ----》触发obj.__enter__(),拿到返回值 5 6 2.as f----->f=返回值、 7 8 3.with obj as f 等同于 f=obj.__enter__() 9 10 4.执行代码块 11 一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None 12 二:有异常的情况下,从异常出现的位置直接触发__exit__ 13 a:如果__exit__的返回值为True,代表吞掉了异常 14 b:如果__exit__的返回值不为True,代表吐出了异常 15 c:__exit__的的运行完毕就代表了整个with语句的执行完毕
软件编程规范之目录管理:
类的装饰器

1 #装饰器在函数中: 2 def deco(func): 3 print('==========') 4 return func 5 6 @deco #相当于test=deco(test) 7 def test(): 8 print('test函数运行') 9 test() 10 输出: 11 ========== 12 test函数运行 13 14 #装饰器在类中也同样适用: 15 def deco(func): 16 print('==========') 17 return func 18 19 @deco #相当于Foo=deco(Foo) 20 class Foo: 21 pass 22 输出: 23 ========== 24 25 #示例: 26 def deco(obj): 27 print('==========',obj) 28 obj.x=1 29 obj.y=2 30 obj.z=3 31 return obj 32 @deco #Foo=deco(Foo) 33 class Foo: 34 pass 35 36 print(Foo.__dict__) 37 输出: 38 ========== <class '__main__.Foo'> 39 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3} 40 41 42 # 一切皆对象: 43 def deco(obj): 44 print('==========',obj) 45 return obj 46 @deco #test=deco(test) 47 def test(): 48 print('test函数') 49 test.x=1 50 test.y=1 51 print(test.__dict__) 52 输出: 53 ========== <function test at 0x000000010D2D4158> 54 {'x': 1, 'y': 1}

1 def Typed(**kwargs): 2 def deco(obj): 3 for key,val in kwargs.items(): 4 setattr(obj,key,val) 5 return 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__) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3} 12 13 @Typed(name='egon') #@deco ---->Bar=deco(Bar) 14 class Bar: 15 pass 16 print(Bar.name) #egon
元类:
什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一一个实例, Foo类是 type类的一个实例)
type是python的一个内建元类 用来直接控制生成类,python中任何class定义的类其实 都是type类实例化的对象
创建类的两种方式
方式一:
class Foo:
def func(se1f) :
print('from func')
方式二:
def func(oo1f) :
printl'from fune')
x=1
Poo=type('Poo' (object,), {'func' :func, 'x':1})
一个类没有声明自己的元类,默认他的元类就是type, 除了使用元类type, 用户也可以通过继承type来自定义元类(顺便我们也可以瞅一-瞅元类如何控制类的创建,工作流程是什么)

1 #元类介绍: 2 class Foo: 3 pass 4 f1=Foo() #f1是通过Foo类实例化的对象 5 6 print(type(f1)) #<class '__main__.Foo'> 7 print(type(Foo)) #<class 'type'> 8 9 class Bar: 10 pass 11 12 print(type(Bar)) #<class 'type'> 13 #自定制元类: 14 15 class MyType(type): 16 def __init__(self,a,b,c): 17 print('元类的构造函数执行') 18 def __call__(self, *args, **kwargs): 19 obj=object.__new__(self) #object.__new__(Foo)-->f1 20 self.__init__(obj,*args,**kwargs) #Foo.__init__(f1,*arg,**kwargs) 21 return obj 22 class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__ 23 def __init__(self,name): 24 self.name=name #f1.name=name 25 26 f1=Foo('alex') 27 print(f1) 28 print(f1.__dict__) 29 输出: 30 元类的构造函数执行 31 <__main__.Foo object at 0x0000007A8A772C88> 32 {'name': 'alex'}