一.类中的内置函数
二.反射
三.包装类型
四.元类
一.OOP中的内置函数
首先明确:类中存在一些名字带有__
(双下滑线)开头的内置函数,这些函数会在某些时候被自动调用,例如之前学习的迭代器,__next__
函数
1.isinstance(obj,cls)
检查是否obj是否是类 cls 的对象
class Foo(object): pass obj = Foo() isinstance(obj, Foo)
2.issubclass(sub, super)
检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
3.1操作对象属性时自动触发
__setattr__
使用点语法添加/修改属性会触发它的执行
__delattr__
使用点语法删除属性的时候会触发
__getattr__
使用点语法调用属性且属性不存在的时候才会触发
__getattribute__
使用点语法调用属性的时候触发,无论属性是否存在都会执行
注意:当__getattribute__
与__getattr__
同时存在时,仅执行__getattribute__
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx #三者的用法演示
3.2操作对象属性时自动触发
__setitem__
使用key的形式添加/修改属性时触发
__getitem__
使用key的形式获取属性时触发
__delitem__
使用key的形式删除属性时触发
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
4.描述符
4.1什么是描述符
描述符本质就是一个类,在这个新式类中,至少实现了__get__()
,__set__()
,__delete__()
中的一个,
也被称为描述符协议 __get__()
:调用一个属性时,触发 __set__()
:为一个属性赋值时,触发 __delete__()
:采用del删除属性时,触发
4.2为什么需要描述符:
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中),python底层的很多特性都是使用描述符来完成的,例如实例方法,classmethod,staticmethod
等等!
简单的说:描述符可以检测到一个属性的访问和修改,从而对这些操作增加额外的功能逻辑;
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age #何地?:定义成另外一个类的类属性 #何时?:且看下列演示 p1=People('alex',18) #描述符Str的使用 p1.name p1.name='egon' del p1.name #描述符Int的使用 p1.age p1.age=18 del p1.age #我们来瞅瞅到底发生了什么 print(p1.__dict__) print(People.__dict__) #补充 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的 print(type(p1).__dict__ == People.__dict__) # 描述符应用 以及执行时机
4.3描述的分类
1.数据描述符 至少实现了__get__()
和__set__()
两个方法
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')
2.非数据描述符 没有实现__set__()
方法
class Foo: def __get__(self, instance, owner): print('get')
4.4注意事项
一 描述符本身应该定义成新式类,被代理的类也应该是新式类 二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中 三 要严格遵循该优先级,优先级由高到底分别是 1.类属性 2.数据描述符 3.实例属性 4.非数据描述符 5.找不到的属性触发__getattr__()
4.5描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是slots属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
案列1:利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)
class Lazyproperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') if instance is None: return self else: print('--->') value=self.func(instance) setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中 return value class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符' def area(self): return self.width * self.length r1=Room('alex',1,1) print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算 实现延迟计算功能
案例2: 利用描述符原理完成一个自定制