一.isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查obj是否是类cls的对象
class Foo(object): pass obj = Foo() print(isinstance(obj,Foo)) # True
issubclass(sub,super)检查sub类是否是super类的派生类
class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar,Foo)) # Bar是否是Foo的子类,True
二.反射
1.什么是反射。
反射的概念是由Smith在1982年首次提出的,主要是指程序可以 访问,检测和修改他本身的状态或行为的一种功能(自省),这一概念的提出很快引发了计算机科学领域关于应用反射性的研究,他首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2.Python面向对象中的反射:通过字符串的形式操作对象相关的属性,Python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法使用于类和对象(一切皆对象,类本身也是一个对象)
判断object中有没有一个name字符串对应的方法或属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
四个方法的使用演示
class School: teach = 'alex' def __init__(self, curriculen, name, addr): self.curriculen = curriculen self.name = name self.addr = addr def python_curriculun(self): print("oldboy学校地址在%s,开始的课程有%s,讲师是%s" %(self.curriculen, self.name, self.addr)) def linux_curriculun(self): print("马哥学校地址在%s,开始的课程有%s,讲师是%s" % (self.curriculen, self.name, self.addr)) python_obj = School('沙河','python','peiqi') linux_obj = School('回龙观','linux','张sir') # 检测是否含有某属性 print(hasattr(python_obj, 'python_curriculun')) # True print(hasattr(python_obj, 'linux_curriculun')) # True # 获取属性 obj = getattr(python_obj, 'python_curriculun') print(obj) # <bound method School.python_curriculun of <__main__.School object at 0x0000000000A546D8>> func = getattr(python_obj,'linux_curriculun') func() # 马哥学校地址在沙河,开始的课程有python,讲师是peiqi # ############## # print(getattr(python_obj,'aaaa')) # 保错 print(getattr(python_obj,'aaaa','没有这个方法,保错啦')) # 没有这个方法,保错啦 # 设置属性 setattr(python_obj,'good',True) setattr(python_obj,'show_name',lambda self:self.name+'good') print(python_obj.__dict__) print(python_obj.show_name(python_obj)) # pythongood # 删除属性 delattr(python_obj,'addr') delattr(python_obj,'show_name') delattr(python_obj,'show_name222') # 不存在就报错 print(python_obj.__dict__)
#静态方法
1 class Foo(object): 2 staticField = 'oldboy' 3 4 def __init__(self): 5 self.name = 'alex' 6 7 def func(self): 8 return 'func' 9 10 @staticmethod # 将该方法变为一个静态方法 11 def bar(): 12 return 'bar' 13 14 print(getattr(Foo,'staticField')) 15 print(getattr(Foo,'func')) 16 print(getattr(Foo,'bar'))
1 #反射当前模块成员 2 import sys 3 4 def s1(): 5 print('s1') 6 7 def s2(): 8 print('s2') 9 10 this_module = sys.modules[__name__] 11 print(hasattr(this_module,'s1')) # True 12 func = getattr(this_module,'s2') 13 func()
导入其他模块,利用反射查找该模块是否存在的某个方法
#module_test.py def test(): print("from the test")
1 ''' 2 程序目录 3 module_test.py 4 5 当前文件oop.py 6 ''' 7 8 import module_test as obj # 重命名 9 10 obj.test() 11 12 print(hasattr(obj, 'test')) # True 13 14 getattr(obj, 'test')() # 获取obj对象的属性,加括号执行
3.为什么用反射之反射的好处
好处一:实现可插拔机制。
例如:有两程序员,一个jack,一个rani,jack中写程序时,需要用到rain所写到的类,但是rani更他老婆度蜜月去了,还没有完成他写的类,于是,jack就想到了反射,使用了反射机制,jack就可以继续完成自己的代码,等rain度蜜月回来以后中继续完成类等定义,并且去实现jack需要的功能。
反射的好处就是:可以事先定义接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种后期绑定,什么意思呢?即是你可以事先把主要的逻辑写好(只定义接口),然后后期在去实现接口的功能
1 # rain还没有实现全部的功能 2 3 class FtpClient: 4 'ftp客户端,但是还没有实现具体的功能' 5 6 def __init__(self, addr): 7 8 print('正在连接服务器%s' %addr) 9 self.addr = addr
# 不影响jack的代码编写 f1 = FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get = getattr(f1,'get') func_get() else: print('----->不存在此方法') print(' 处理其他逻辑')
好处二:动态导入模块(基于反射当前模块成员)
三.__setattr__,__delattr__,__getaddr__
三者的用法演示
class Foo: a = 1 def __init__(self,y): self.y = y def __getattr__(self, item): print('----->from getattr:你想要获取的属性不存在') def __setattr__(self, key, value): print('---->form setattr') # self.key = value # 这就无限递归下去了,因为,你每次进行添加或修改时,就会触发 __setattr__,所以就会死循环 self.__dict__[key] = value def __delattr__(self, item): print('----->from delattr') # del self.item # 无限递归了,原理同上 self.__dict__.pop(item) # __setattr__添加/修改属性会触发他的执行 obj = Foo(10) obj.a # ---->form setattr # ----->from getattr:你想要获取的属性不存在 print(obj.__dict__) # {'y': 10} 因为你重写了__setattr__,凡是赋值操作都会触发他的运行,你啥都没写, # 就是根本没赋值,除非,你直接操作属性字典,否则永远无法赋值 obj.z = 3 print(obj.__dict__) # {'y': 10, 'z': 3} # __delattr__ 删除属性的时候会触发 obj.__dict__['a'] = 12 # 我们可以直接修改属性字典,来完成添加/修改属性的操作 del obj.a # ----->from delattr print(obj.__dict__) # {'z': 3, 'y': 10} ## __getattr__只有当你使用调用属性且属性不存在的时候才触发 obj.aaaa # ----->from getattr:你想要获取的属性不存在
四.二次加工标准类型(包装)
包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了以上所写的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
1 class List(list): 2 def append(self, p_object): 3 '我改写的方法' 4 if not isinstance(p_object,str): 5 print('只有字符串类型能被添加到列表中') 6 return 7 super().append(p_object) 8 9 def show_mid(self): 10 '我新增的方法' 11 index = int(len(self)/2) 12 print(self[index]) 13 14 l1 = List('hello') 15 l1.append('abc') 16 print(l1,type(l1)) # ['h', 'e', 'l', 'l', 'o', 'abc'] <class '__main__.List'> 17 18 #数字无法添加成功 19 l1.append(12) # 只有字符串类型能被添加到列表中 20 21 # 基于标准类型新增了功能 22 l1.show_mid() # l 获取字符串中间元素
授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或者删除原有产品的功能,其他的则保持原样,授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性
实现授权的关键点就是覆盖__getattr__方法
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 6 def wirit(self,line): 7 t = time.strftime('%Y-%m-%d %X') 8 self.file.write('%s %s' %(t,line)) 9 10 def __getattr__(self, item): 11 return getattr(self.file,item) 12 13 obj = FileHandle('a.txt','w+') 14 obj.wirit('hello') 15 obj.seek(0) 16 obj = FileHandle('a.txt','r+') 17 print(obj.read()) # 2016-12-25 16:21:38 hello 18 obj.close()
1 import time 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.encoding=encoding 9 10 def write(self,line): 11 t=time.strftime('%Y-%m-%d %T') 12 if 'b' in self.mode: 13 msg=bytes('%s %s' %(t,line),encoding=self.encoding) 14 self.file.write(msg) 15 16 def __getattr__(self, item): 17 return getattr(self.file,item) 18 19 f1=FileHandle('b.txt','wb') 20 f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气 21 f1.close()
五.__getattribute__
1 # 回顾__getattr__ 2 class Foo: 3 def __init__(self,x): 4 self.x = x 5 6 def __getattr__(self, item): 7 print('执行的是我') 8 9 obj = Foo(10) 10 print(obj.x) 11 obj.xxx # 不存在是属性访问,会触发__getattr__
1 # __getattribute__ 2 3 class Foo: 4 def __init__(self,x): 5 self.x = x 6 7 def __getattribute__(self, item): 8 print('不管是否存在,我都会执行') 9 10 obj = Foo(10) 11 obj.x # 不管是否存在,都会执行__getattribute__ 12 obj.xxxxx # 不管是否存在,都会执行__getattribute__
# 如果两者同时出现 class Foo: def __init__(self,x): self.x = x def __getattr__(self, item): print('执行__getattr__') # return self.__dict__[item] def __getattribute__(self, item): print('不管是否存在,我都会执行') raise AttributeError('抛出异常啦') obj = Foo(10) obj.x obj.xxxxx #当__getattribute__与__getattr__同时存在,只会执行__getattribute__,除非__getattribute__的执行过程中抛出异常AttributeError,__getattr__会接收这个异常
六.描述符(__get__,__set__,__delete__)
1.描述符:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。
__get__():调用一个属性时触发。
__set__():为一个属性赋值时触发。
__delete__():采用del删除属性时触发。
1 # 定义一个描述符 2 class Foo: 3 def __get__(self, instance, owner): 4 pass 5 6 def __set__(self, instance, value): 7 pass 8 9 def __delete__(self, instance): 10 pass
2.描述符的作用:描述符的作用是用来代理另外一个类的属性(必须把描述符定义成这个类的属性,不能定义到构造函数中)
1 # 示例,描述符产生的实例进行属性操作并不会触发三个方法的执行 2 3 class Foo: 4 def __get__(self, instance, owner): 5 print('触发__get__') 6 7 def __set__(self, instance, value): 8 print('触发__set__') 9 10 def __delete__(self, instance): 11 print('触发__delete__') 12 13 # 包含这三个方法的新式类称为描述符,有这个类产生的实例,进行属性的调用/赋值/删除,并不会触发这三个方法 14 obj = Foo() 15 obj.name = 'jack' 16 print(obj.name) 17 del obj.name
1 # 描述符应用 2 3 # 描述符Str 4 5 class Str: 6 def __get__(self, instance, owner): 7 print('Str调用---') 8 9 def __set__(self, instance, value): 10 print('Str设置---') 11 12 def __delete__(self, instance): 13 print('Str删除---') 14 15 # 描述符Int 16 class Int: 17 def __get__(self, instance, owner): 18 print('Int调用---') 19 20 def __set__(self, instance, value): 21 print('Int设置---') 22 23 def __delete__(self, instance): 24 print('Int删除---') 25 26 class People: 27 name = Str() 28 age = Int() 29 30 def __init__(self, name, age): # name被Str类代理,age被Int类代理 31 self.name = name 32 self.age = age 33 34 obj = People('jack',18) # Str设置--- Int设置--- 35 36 # 描述符Str的使用 37 obj.name # Str调用--- 38 obj.name = 'alex' # Str设置--- 39 del obj.name # Str删除--- 40 41 # 接下来我们来看看到底发生了什么事 42 print(obj.__dict__) # 由此可以看出,obj中的name,age属性,会去找Str,Int描述符,所以obj.__dict__为{} 43 print(People.__dict__) 44 45 # 补充 46 print(type(obj) == People) # True typy(obj)其实是查看obj是有哪个实例化来的 47 print(type(obj).__dict__ == People.__dict__) # True
3.描述符分为两种。
一.数据描述符:至少实现了__get__()和__set__()
1 class Foo: 2 3 def __set__(self, instance, value): 4 print('__set__') 5 6 def __get__(self, instance, owner): 7 print('__get__')
二.非数据描述符:没有实现__set__()
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
4.注意事项
一.描述符本身应该定义成新式类,被代理的类也应该是新式类。
二.必须把描述符定义成这个类的类属性,不能为定义到构造函数中。
三.要严格遵循该优先级,优先级由高到低分别是:
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
# 优先级,类属性>数据描述符 # 描述符Str class Str: def __get__(self, instance, owner): print('Str调用---') def __set__(self, instance, value): print('Str设置---') def __delete__(self, instance): print('Str删除---') class People: name = Str() def __init__(self, name, age): self.name = name self.age = age # 基于上面的演示,我们已经知道,在一个类中定义一个描述符他就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 # 那么既然描述符被定义成了一个类属性,就可以直接通过类名也可以调用 People.name # Str调用--- 调用类属性name,本质上就是在调用描述符Str,触发了__get__() # ---------------------------- People.name = 'alex' del People.name # 为什么我的赋值和删除操作没有触发到描述符的__set__(),__delete__()方法呢? """ 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来 的类属性有更高的优先级, People.name 是调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() # People.name = 'alex' # 那么赋值呢?直接赋值了一个类属性,他拥有更高的优先级,相当于覆盖了 描述符,所以肯定就不会触发描述符的__set__()方法 del People.name 原理同上 """
1 # 优先级 数据描述符>实例属性 2 3 # 描述符Str 4 class Str: 5 def __get__(self, instance, owner): 6 print('Str调用---') 7 8 def __set__(self, instance, value): 9 print('Str设置---') 10 11 def __delete__(self, instance): 12 print('Str删除---') 13 14 class People: 15 name = Str() 16 17 def __init__(self, name, age): 18 self.name = name 19 self.age = age 20 21 obj = People('jack',20) 22 ''' 23 如果描述符是一个数据描述符(既有__get__又有__set__), 24 那么obj.name的调用与赋值都是触发描述符的操作,于obj本身无关了,相当于覆盖了实例的属性 25 ''' 26 obj.name = 'alex' 27 obj.name 28 print(obj.__dict__) 29 ''' 30 {'age': 20}实例属性字典中没有name,因为name是一个数据描述符,优先级要高于实例属性, 31 查看/赋值/删除都是跟描述符有关,与实例无关 32 ''' 33 del obj.name
1 # 优先级 实例属性>非数据描述符 2 3 class Foo: 4 5 def func(self): 6 print('哈哈哈,我又回来了') 7 8 obj = Foo() 9 obj.func() # 调用类的方法,也可以说是调用非数据描述符 10 # 函数是一个非数据描述符对象(python中一切皆对象) 11 print(dir(Foo.func)) 12 print(hasattr(Foo.func,'__set__')) # False 13 print(hasattr(Foo.func,'__get__')) # True 14 print(hasattr(Foo.func,'__delete__')) # False 15 # 在这里有人可能会问了,描述符不是类的吗,函数怎么算也应该是一个对象呀,怎么就是描述符了 16 # 描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性了吗 17 # 函数就是一个由非描述符实例化得到的对象 18 # 没错,字符串也一样 19 20 obj.func = '这就是实例属性啊' 21 print(obj.func) # 这就是实例属性啊 22 23 del obj.func 24 obj.func() # 哈哈哈,我又回来了
1 class Foo: 2 3 def __set__(self, instance, value): 4 print('__set__==========') 5 6 def __get__(self, instance, owner): 7 print('__get__========') 8 9 class Room: 10 name = Foo() 11 12 def __init__(self, name, width, length): 13 self.name = name 14 self.width = width 15 self.length = length 16 17 # name是一个数据描述符,因为name=Foo(),而Foo实现了get和set方法,因为比实例属性有更高的优先级 18 # 对实例的属性操作,触发的都是描述符的 19 r1 = Room('卧室',20,10) # 触发__set__========== 20 r1.name # 触发__get__======== 21 r1.name = '厨房' # 触发__set__========== 22 23 24 class Foo: 25 26 def __get__(self, instance, owner): 27 print('__get__') 28 29 class Room: 30 name = Foo() 31 32 def __init__(self,name,width,length): 33 self.name = name 34 self.width = width 35 self.length = length 36 37 # name是一个非数据描述符,因为name=Foo(),而Foo没有实现set方法,因此比实例属性有更低的优先级 38 # 对实例的属性操作,触发的都是实例自己的 39 40 r1 = Room('卧室',20,10) 41 r1.name 42 r1.name = '厨房'
5.描述符使用
Python是弱类型语言,即参数的赋值没有类型的限制,下面我们通过描述符机制来实现类型限制功能。
1 # #########################示例一 2 3 class Str: 4 def __init__(self, name): 5 self.name = name 6 7 def __get__(self, instance, owner): 8 print('15__get__',instance, owner) # instance是引用描述符的对象,owner是引用描述符的类 9 return instance.__dict__[self.name] 10 11 def __set__(self, instance, value): 12 print('19__set__',instance, value) 13 instance.__dict__[self.name] = value 14 15 def __delete__(self, instance): 16 print('23__delete__',instance) 17 instance.__dict__.pop(self.name) 18 19 20 class People: 21 name = Str('name') 22 def __init__(self, name, age, salary): 23 self.name = name 24 self.age = age 25 self.salary = salary 26 27 obj = People('jack',23,30000) 28 29 # 调用 30 print(obj.__dict__) 31 print(obj.name) #触发__get__ 32 33 # 赋值 34 print(obj.__dict__) 35 obj.name = 'laiying' 36 print(obj.__dict__) # 触发__set__ 37 38 # 删除 39 print(obj.__dict__) 40 del obj.name # 触发__delete__ 41 print(obj.__dict__)
# #############示例二 修订版一 # ############# 疑问:如果我用类名去操作属性呢。 class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print('15__get__',instance, owner) # instance是引用描述符的对象,owner是引用描述符的类 return instance.__dict__[self.name] def __set__(self, instance, value): print('19__set__',instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print('23__delete__',instance) instance.__dict__.pop(self.name) class People: name = Str('name') def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary # 疑问:如果我用类名去操作属性呢。 People.name # 报错,错误的根源在于类去操作属性时,会把None传给instance # 修订_get__方法 (在__get__这个方法中加上一个条件判断) class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print('15__get__',instance, owner) # instance是引用描述符的对象,owner是引用描述符的类 print('87---',self) # self 描述符对象自己 if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('19__set__',instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print('23__delete__',instance) instance.__dict__.pop(self.name) class People: name = Str('name') def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary People.name
1 # ##########示例三 2 #修订版二 如果传入的参数不是期望的类型,则抛出异常 3 4 class Str: 5 def __init__(self, name, type): 6 self.name = name 7 self.type = type 8 9 def __get__(self, instance, owner): 10 print('15__get__',instance, owner) # instance是引用描述符的对象,owner是引用描述符的类 11 print('87---',self) # self 描述符对象自己 12 if instance is None: 13 return self 14 return instance.__dict__[self.name] 15 16 def __set__(self, instance, value): 17 if not isinstance(value, self.type): 18 print('130------',value,self.type) # value(传入的参数),self.type参数的指定类型 19 raise TypeError('类型错误%s'%str(self.type)) 20 instance.__dict__[self.name] = value 21 22 def __delete__(self, instance): 23 print('23__delete__',instance) 24 instance.__dict__.pop(self.name) 25 class People: 26 name = Str('name',str) # 限制传入参数类型 27 def __init__(self, name, age, salary): 28 self.name = name 29 self.age = age 30 self.salary = salary 31 32 obj = People(1,28,30000)
# 修订版三,如果我们的需求是每个传入参数的类型都不相同,怎么办
1 class Typed: 2 def __init__(self, name, type): 3 self.name = name 4 self.type = type 5 6 def __get__(self, instance, owner): 7 print('15__get__',instance, owner) # instance是引用描述符的对象,owner是引用描述符的类 8 print('87---',self) # self 描述符对象自己 9 if instance is None: 10 return self 11 return instance.__dict__[self.name] 12 13 def __set__(self, instance, value): 14 if not isinstance(value, self.type): 15 print('130------',value,self.type) # value(传入的参数),self.type参数的指定类型 16 raise TypeError('类型错误%s'%str(self.type)) 17 instance.__dict__[self.name] = value 18 19 def __delete__(self, instance): 20 print('23__delete__',instance) 21 instance.__dict__.pop(self.name) 22 class People: 23 name = Typed('name',str) # 限制传入参数类型 24 age = Typed('age',int) 25 salary = Typed('salary',float) 26 def __init__(self, name, age, salary): 27 self.name = name 28 self.age = age 29 self.salary = salary 30 31 obj = People(11,28,30000) 32 obj = People('laiying',28,30000) 33 obj = People('laiying','28',33000.8) 34 obj = People('laiying',28,33000.8)
通过以上方法我们实现了给传入参数进行类型的限制,但是问题是,如果我们的类有很多属性,仍采用在类中定义一堆的类属性去实现,这样显得不够灵活,工作量大
在这里我们可以使用类的装饰器去实现多个参数的类型限制,而不用在类中去定义一大堆属性
一.类的装饰器:无参
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('laiying',18,3333.3) # 类的装饰器开始运行啦-----------
二.类的装饰器:有参
1 def typeassert(**kwargs): 2 def decorate(cls): 3 print('类的装饰器开始运行啦-----------') 4 return cls 5 return decorate 6 7 # 有参:1,运行typeassert(...),返回结果是decorate,此时参数都给了kwargs, 8 # 2,People = decorate(People) 9 @typeassert(name=str,age=int,salary=float) 10 class People: 11 def __init__(self, name, age, salary): 12 self.name = name 13 self.age = age 14 self.salary = salary 15 16 obj = People(11,28.4,30000) # 类的装饰器开始运行啦-----------
三.终极大招,通过类的装饰器完成传入参数类型的限定
class Typed: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): if not isinstance(value, self.expected_type): raise TypeError('类型错误%s'%str(self.expected_type)) instance.__dict__[self.name] = value def __delete__(self, instance): print('23__delete__',instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print('类的装饰器开始运行啦-----------') for key,v in kwargs.items(): # key(传入的参数名),v(参数的指定类型) 如:name=int #cls()被装饰的类,People,Typed(key,v)(描述符的对象) setattr(cls,key,Typed(key,v)) print('178====',cls,key,Typed(key,v)) return cls return decorate # 有参:1,运行typeassert(...),返回结果是decorate,此时参数都给了kwargs, # 2,People = decorate(People) @typeassert(name=str,age=int,salary=float) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary obj = People('jack',28,30000.00) # 类的装饰器开始运行啦-----------
6.描述符总结
描述符是可以实现大部分Python类特性中的底层魔法,包括@classmethod,@staticmentd,@properyt甚至是__slots__属性
描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件
7.利用描述符原理完成一个自定制@property,实现延迟计算(本质上就是把一个函数属性利用装饰器原理做成了一个描述符:类的属性字典中函数名为Key,value为描述符产生的对象)
7.1 @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 obj = Room('jack',20,30) 12 print(obj.area) 13 obj.area = 10 # 无法设置,报错
利用描述符原理,自己做一个@property
1 class Lazyproperyt: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 print('289这是我们自己定制的静态属性,obj.area实际是要执行obj.area()') 7 if instance is None: 8 return self 9 return self.func(instance) # 执行func函数,func=作为参数传入的函数,instance=被描述的对象 10 11 class Room: 12 def __init__(self, name, width, length): 13 self.name = name 14 self.width = width 15 self.length = length 16 17 @Lazyproperyt 18 def area(self): 19 return self.width * self.length 20 21 obj = Room('jack',20,20) 22 print(obj.area)
实现一个延迟计算功能
1 class Lazyproperyt: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 print('289这是我们自己定制的静态属性,obj.area实际是要执行obj.area()') 7 if instance is None: 8 return self 9 else: 10 print('------------') 11 value = self.func(instance) 12 setattr(instance,self.func.__name__,value) 13 # instance=obj对象 self.func.__name__=函数名 value=函数执行结果 14 print('296--',instance,self.func.__name__,value) 15 return value 16 17 class Room: 18 def __init__(self, name, width, length): 19 self.name = name 20 self.width = width 21 self.length = length 22 23 @Lazyproperyt 24 def area(self): 25 return self.width * self.length 26 27 obj = Room('jack',20,20) 28 print(obj.area) # 先从自己的属性字典找,没有再去类的属性字典找,然后触发了area的__get__方法 29 print(obj.area) # 先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算一次
哈哈哈,延迟计算失败了把
1 class Lazyproperyt: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 print('289这是我们自己定制的静态属性,obj.area实际是要执行obj.area()') 7 if instance is None: 8 return self 9 else: 10 print('------------') 11 value = self.func(instance) 12 setattr(instance,self.func.__name__,value) 13 # instance=obj对象 self.func.__name__=函数名 value=函数执行结果 14 print('296--',instance,self.func.__name__,value) 15 return value 16 17 def __set__(self, instance, value): 18 print('__set__') 19 20 class Room: 21 def __init__(self, name, width, length): 22 self.name = name 23 self.width = width 24 self.length = length 25 26 @Lazyproperyt 27 def area(self): 28 return self.width * self.length 29 30 obj = Room('jack',20,20) 31 print(Room.__dict__) 32 print(obj.area) 33 print(obj.area) 34 # 缓存功能失效了,每次都去找描述符了,为什么,因为描述符实现了__set__方法, 35 #他由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因此所有的属性都去找描述符了
7.2 @staticmethod静态方法回顾
1 class Room: 2 def __init__(self, name, owner, width, length): 3 self.name = name 4 self.owner = owner 5 self.width = width 6 self.length = length 7 8 @property 9 def area(self): 10 return self.width * self.length 11 12 @staticmethod # 静态方法类和实例都可以调用 13 def foo(): 14 print('洗刷刷,洗刷刷') 15 16 def test(): 17 print('这不是静态方法,用类调用没问题,你用实例调用看看') 18 19 Room.foo() 20 21 obj = Room('卧室','jack',20,20) 22 obj.foo() 23 24 Room.test() 25 obj.test() 26 # 报错,因为如果test不是静态方法的话,那么,obj会把自己传给test的第一个参数self, 27 #test无参所以报错
利用描述符原理完成一个自定制@staticmethod
1 class StaticMethod: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 def feedback(*args,**kwargs): 7 print('在这里可以加功能a-----') 8 return self.func(*args, **kwargs) 9 return feedback 10 11 class People: 12 @StaticMethod 13 def foo(x,y,z): 14 print('foo------->',x,y,z) 15 16 People.foo(11,22,33) 17 obj = People() 18 obj.foo(1,2,3)
7.3@classmethod回顾
1 class Room: 2 style = '欧式豪宅 ' 3 def __init__(self, name, owner, width, length): 4 self.name = name 5 self.owner = owner 6 self.width = width 7 self.length = length 8 9 @property 10 def area(self): 11 return self.width * self.length 12 13 @staticmethod # 静态方法类和实例都可以调用 14 def foo(): 15 print('洗刷刷,洗刷刷') 16 17 def test(): 18 print('这不是静态方法,用类调用没问题,你用实例调用看看') 19 20 @classmethod 21 def tell_style(cls): 22 print('房间风格是%s' %cls.style) # 不能访问实例 23 24 Room.tell_style() # 类方法的定义应该只是为了类去调用 25 obj = Room('卧室','jack',20,20) 26 obj.tell_style() # 但是其实你非要让实例去调用,也是可以的,不过却违反了我们的初衷,类方法就是专门给类使用的
利用描述符原理完成一个自定制@classmethod
1 class ClassMethod: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): 6 def feedback(): 7 print('这里可以加功能啊------') 8 return self.func(owner) 9 return feedback 10 11 class People: 12 name = 'jack' 13 @ClassMethod 14 def say_hi(cls): 15 print('您好啊,%s' %cls.name) 16 17 People.say_hi() 18 obj = People() 19 obj.say_hi() 20 #疑问,类方法如果有参数呢, 21 22 class ClassMethod: 23 def __init__(self,func): 24 self.func = func 25 26 def __get__(self, instance, owner): 27 def feedback(*args,**kwargs): 28 print('这里可以加功能啊------') 29 return self.func(owner,*args,**kwargs) 30 return feedback 31 32 class People: 33 name = 'jack' 34 @ClassMethod 35 def say_hi(cls,msg): 36 print('您好啊,%s %s' %(cls.name,msg)) 37 38 People.say_hi('在干嘛呢') 39 obj = People() 40 obj.say_hi('hhh')
六在看property
一个静态属性propery本质就是实现了get,set,delete三种方法
6.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 f = Foo() 15 f.AAA 16 f.AAA = 'aaa' 17 del f.AAA
用法二
1 class Foo: 2 3 def get_AAA(self): 4 print('get的时候运行我啊') 5 6 def set_AAA(self,value): 7 print('set的时候运行我啊') 8 9 def delete_AAA(self): 10 print('delete的时候运行我啊') 11 12 AAA = property(get_AAA,set_AAA,delete_AAA) 13 14 f = Foo() 15 f.AAA 16 f.AAA = 'aaa' 17 del f.AAA
怎么用?
案例一
1 class Goods: 2 def __init__(self): 3 self.original_price = 100 # 原价 4 self.discount = 0.7 # 折扣 5 6 @property 7 def price(self): 8 # 实际价格 = 原价 * 折扣 9 new_price = self.original_price * self.discount 10 return new_price 11 12 @price.setter 13 def price(self,value): 14 self.original_price = value 15 16 @price.deleter 17 def price(self): 18 del self.original_price 19 20 obj = Goods() 21 print(obj.price) # 获取商品价格 22 obj.price = 200 # 修改商品原价 23 print(obj.price) 24 del obj.price # 删除商品原价
案例二
#########实现类型检查功能
第一关
class People:
def __init__(self, name):
self.name = name
@property
def name(self):
return self.name
obj = People('jack')
property自动实现了set和get方法属性,属于数据描述符。比实例属性优先级高,
所以在这会触发property内置的set,抛出异常
# 第二关:修订版 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan obj = People('alex') print(obj.name) print(obj.name) print(obj.name) print(obj.__dict__) obj.name = 'rain' print(obj.__dict__) del obj.name
#第三关,实现类型检查
class People:
def __init__(self,name):
self.name=name #实例化就触发property
@property
def name(self):
# return self.name #无限递归
print('get------>')
return self.DouNiWan
@name.setter
def name(self,value):
print('set------>')
if not isinstance(value,str):
raise TypeError('必须是字符串类型')
self.DouNiWan=value
@name.deleter
def name(self):
print('delete------>')
del self.DouNiWan
obj = People('jack')
obj.name = 1
七: __setitem__,__getitem__,__delitem__
1 class Foo: 2 3 def __init__(self,name): 4 self.name = name 5 6 def __getitem__(self, item): 7 print('__getitem__',item) 8 # 通过obj['对象属性']时触发 9 print(self.__dict__[item]) 10 11 def __setitem__(self, key, value): 12 print('__setitem__', self,key, value) 13 # self对象本身,key=key,value=value 14 self.__dict__[key] = value 15 16 def __delitem__(self, key): 17 print('del obj[key]时,执行',key) 18 # 通过del obj[key]时触发 19 self.__dict__.pop(key) 20 21 def __delattr__(self, item): 22 print('del obj.key时,我执行',item) 23 # 通过obj.key时,触发 24 self.__dict__.pop(item) 25 26 obj = Foo('jack') 27 obj['age'] = 20 28 obj['name'] 29 # del obj['age'] 30 obj['name'] = 'rain' 31 print(obj.__dict__)
八.__str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
1 format_dic = { 2 'nat':'{obj.name}-{obj.addr}-{obj.type}', # 学校名-学校地址-学校类型 3 'tna':'{obj.name}:{obj.addr}:{obj.type}', # 学校名:学校地址:学校类型 4 'tan':'{obj.name}/{obj.addr}/{obj.type}', # 学校名/学校地址/学校类型 5 } 6 7 class School: 8 9 def __init__(self, name, addr, type): 10 self.name = name 11 self.addr = addr 12 self.type = type 13 14 def __repr__(self): 15 return '__repr__===School(%s %s)' %(self.name, self.addr) 16 17 def __str__(self): 18 return '__str__===(%s %s)' %(self.name, self.addr) 19 20 def __format__(self, format_spec): 21 print('82format_spec',format_spec) 22 # format_spec等于传入的参数 23 if not format_spec or format_spec not in format_dic: 24 format_spec = 'nat' 25 # 如果传入的参数不存在,就设置一个默认格式 26 27 fmt = format_dic[format_spec] 28 print('87fmt===',fmt) 29 # 通过传入参数key,取得对应的vaule 30 fmt.format(obj=self) 31 return fmt.format(obj=self) 32 33 obj = School('清华大学','北京','公立') 34 print(repr(obj)) # __repr__===School(清华大学 北京) 35 print(str(obj)) # __str__===(清华大学 北京) 36 print(obj) # __str__===(清华大学 北京) 37 ''' 38 str函数或者print函数--->obj.__str__() 39 repr函数或者交互式解释器--->obj.__repr__() 40 如果__str__没有被定义,那么就会使用__repr__来代替输出 41 注意:这两个方法的返回值必须是字符串,否则抛出异常 42 ''' 43 print(format(obj,'nat'))
format练习
1 date_dic={ 2 'ymd':'{0.year}:{0.month}:{0.day}', 3 'dmy':'{0.day}/{0.month}/{0.year}', 4 'mdy':'{0.month}-{0.day}-{0.year}', 5 } 6 class Date: 7 def __init__(self,year,month,day): 8 self.year=year 9 self.month=month 10 self.day=day 11 12 def __format__(self, format_spec): 13 if not format_spec or format_spec not in date_dic: 14 format_spec='ymd' 15 fmt=date_dic[format_spec] 16 return fmt.format(self) 17 18 d1=Date(2016,12,29) 19 print(format(d1)) 20 print('{:mdy}'.format(d1))
issubclass和isinstance
1 class A: 2 pass 3 4 class B(A): 5 pass 6 obj = B() 7 print(isinstance(obj,B)) # 判断obj是否是B的实例 8 print(issubclass(B,A)) # 判断B是否是A的子类,是返回True,不是返回False
九.__slots__
__slots__使用
1 ''' 2 1.__slots__:是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串, 3 (意味这所有实例只有一个类属性) 4 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例是独立的) 5 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例, 6 为了节省内存可以使用__slots__取代实例的__dict__ 7 当你定义__slots__后,__slots_就会为实例使用一种更加的紧凑的内部表示,实例通过一个很小的固定大小 8 的数组来构建,而不是为每个实例定义一个 9 字典,这跟元组或列表很类似,在__stots__中列出的属性名在内部被隐射到这个数组的指定小标上, 10 11 使用__slots__一个不好的地方就是我们不能在给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 12 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现,另外,定义了__slots__后的类不再 13 支持一些普通类特性了,比如:多继承, 14 大多数情况下,你应该 15 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 16 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的, 17 但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。 18 ''' 19 20 class Foo: 21 __slots__ = 'x' 22 23 obj = Foo() 24 obj.x = 22 25 obj.y = 33 # 报错,不让添加属性 26 print(obj.__slots__) # obj不在有__dict__ 27 # 28 class Bar: 29 __slots__ = ['a','y'] 30 31 obj_bar = Bar() 32 obj_bar.a,obj_bar.y = 'jack','23' 33 obj.b = 'rain' # 报错 34 print(obj_bar.__slots__)
1 class Foo: 2 __slots__ = ['name','age'] 3 4 obj = Foo() 5 obj.name = 'jack' 6 obj.age = 18 7 print(obj.__slots__) 8 9 obj_foo = Foo() 10 obj_foo.name = 'alex' 11 obj_foo.age = 30 12 print(obj_foo.__slots__) 13 14 print(Foo.__dict__) 15 # obj和obj_foo都没有属性字典__dict__了,统一归__slots__管,节省内存
十.__next__和__iter__实现迭代器协议
1 class Foo: 2 def __init__(self,x): 3 self.x = x 4 5 def __iter__(self): 6 return self 7 8 def __next__(self): 9 self.x +=1 10 if self.x == 100: 11 raise StopIteration 12 return self.x 13 14 obj = Foo(10) 15 for i in obj: 16 print(i)
1 # 斐波那契数列 2 3 class Fib: 4 def __init__(self): 5 self._a = 0 6 self._b = 1 7 8 def __iter__(self): 9 return self 10 11 def __next__(self): 12 self._a,self._b = self._b,self._a + self._b 13 return self._b 14 obj = Fib() 15 print(obj.__next__()) 16 print(next(obj)) 17 print(next(obj)) 18 print(next(obj)) 19 print(next(obj))
十一.__doc__
__doc__类的描述信息
1 class Foo: 2 '我是Foo类的描述信息' 3 pass 4 5 print(Foo.__doc__)
该属性无法被继承
class Foo: '我是Foo类的描述信息' pass class Bar(Foo): pass print(Foo.__doc__) # 我是Foo类的描述信息 print(Bar.__doc__) # None
十二.__module__和__class__
__module__表示当前操作的对象在那个模块
__class__表示当前操作对象的类是什么
1 class A: 2 def __init__(self): 3 self.name = 'jack' 4 5 obj = A() 6 print(obj.__class__) # <class '__main__.A'> 7 8 from homework.test.one import Foo 9 10 f = Foo() 11 print(f.__module__) # homework.test.one
十三.__del__
析构方法,当对象在内存中被释放时,自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行的,
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') obj = Foo() del obj print('-------------')
十四.__enter__和__exit__
当我们在操作文件对象的时候可以这么写
1 with open('a.txt') as f: 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 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 print('with中代码块执行完毕时执行我') 10 11 with Open('a.txt') as f: 12 print('===>执行代码块')
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中的代码块出现异常,则with后的代码都无法执行
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 5 def __enter__(self): 6 print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') 7 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 print('with中代码块执行完毕时执行我') 10 print(exc_type) 11 print(exc_val) 12 print(exc_tb) 13 14 with Open('a.txt') as f: 15 print('===>执行代码块') 16 raise AttributeError('====找火了====') 17 print('0'*100) # ---------->不会执行
如果__exit()返回值为True,那么异常会被情况,就好像啥都没发生一样,with后的语句正常执行
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 5 def __enter__(self): 6 print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') 7 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 print('with中代码块执行完毕时执行我') 10 print(exc_type) 11 print(exc_val) 12 print(exc_tb) 13 return True 14 15 with Open('a.txt') as f: 16 print('===>执行代码块') 17 raise AttributeError('====找火了====') 18 print('0'*100) # ---------->不会执行
用途:
1.使用with语句的目的,就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十五.__call__
对象后面加括号,触发执行
注:构造方法的执行是由创建对象触发的,即:对象=类名(),而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()
1 class Foo: 2 3 def __init__(self): 4 print('__init__') 5 pass 6 7 def __call__(self, *args, **kwargs): 8 print('__call__') 9 10 obj = Foo() # 执行__init__ 11 obj() # 执行__call__
十六 metaclass
1.引子
class Foo: pass obj = Foo() # obj是通过Foo类实例化的对象
Python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,Python解释器在加载class的时候就会创建一个对象(这里的对象指的是类。而非类的实例)
从上述例子中可以看出obj是由Foo这个类产生的对象,而Foo本身也是对象,那么他是由哪个类产生的呢?
# type函数可以查看类型,也可以用来查看对象的类,两者都是一样的
1 class Foo: 2 pass 3 obj = Foo()
4 print(type(obj)) #<class '__main__.Foo'>
5 print(type(Foo)) # <class 'type'>
2.什么是元类
元类是类的类,是类的模版
元类是用来如何创建类的,正如类是创建对象的模版一样
元类的实例为类,正如类的实例为对象(obj对象是Foo类的一个实例,Foo类是type类的一个实例)
type是Python的一个内建元类,用来直接控制生成类,Python中任何class定义的类,其实都是type实例化的对象
3.创建类的两种方式
方式一:
1 class Foo: 2 def func(self): 3 print('from func')
方式二:
def __init__(self,name,age):
self.name = name
self.age = age
def test():
pass
Foo=type('Foo',(object,),{'x':1,'__init__':__init__,'test':test})
print(Foo.__dict__)
obj = Foo('alex',25)
print(obj.name)
4.一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以继承type来自定义元类。
一.自定制元类
1 class Mytype(type): 2 def __init__(self,a,b,c): # self = Foo 3 print(a) # Foo 4 print(b) # ()元组 5 print(c) # {'__init__': <function Foo.__init__ at 0x0000000001190378>, '__module__': '__main__', '__qualname__': 'Foo'} 6 print('元类的构造函数执行') 7 8 def __call__(self, *args, **kwargs): 9 obj = object.__new__(self) # object.__new__(Foo)-->f1 10 self.__init__(obj,*args,**kwargs) # Foo.__init__(f1,*args,**kwargs) 11 return obj 12 13 14 class Foo(metaclass=Mytype): # Foo=Mytype(Foo,'Foo',(),{})-->__init__ 15 def __init__(self,name): 16 self.name = name 17 18 f1 = Foo('laiying')