关于__setitem__,__getitem__,delitem__
类似于以前的学过的__setattr__,__getattr__...
不同之处在于item结尾的是用于对象以字典添加的形式添加,查看或者删除属性的时候才会触发,如下例子:
class Foo(object): # __slots__=['x','y'] def __setitem__(self, key, value): print('我在写入') self.__dict__[key]=value def __getitem__(self, item): print('我在返回值') return self.__dict__[item] def __delitem__(self, key): print('我在删除值') del self.__dict__[key] a=Foo() a['x']=1 print(a['x']) del a['x']
接下来我们来看看__slots__:
__slots__有两个作用
作用一:
__slots__=['x','y']
如果在类中定义了以上的属性那么就限制了实例化对象的添加成员
如下例子:
class Foo(object): __slots__=['x','y','z'] def run(self): print('from run') a=Foo() a.x=10 a.y=20 a.z=12 a.w=13
通过运行我们发现当我们尝试设置w属性的时候就报错了
作用二:
我们在类中添加__slots__后执行 对象.__dict__发现对象不在产生dict了,也就是说对象不再独立开辟对象自己的命名空间,由此可以看出在类中限制好对象的成员后,不仅可以限制对象添加成员还可以以此节约内存空间的目的
class Foo(object): __slots__=['x','y','z'] def run(self): print('from run') a=Foo() print(a.__dict__) b=Foo() print(b.__dict__)
可能你现在还无法想象,加入我们每实例化一个对象就会产生一个dict,如果有成百上前的对象就会产生成百上千的独立命名空间,这样就会浪费很多的内存空间
迭代器的原理:
你有想过迭代器是如何实现的吗,假设我们要自定义一个range函数,这个时候我们就需要用到__next__和__iter__了
我们都是知道只要是包含__iter__方法的就是一个可迭代对象,执行__next__方法就会返回一个值,那么我们模拟下range函数
class Range(object): def __init__(self,start,stop,jump=0): self.start=start self.stop=stop self.jump=jump if self.jump>0: self.jump=self.jump-1 def __iter__(self): return self def __next__(self): n=self.start if self.start>self.stop-1: raise StopIteration self.start+=(1+self.jump) return n for i in Range(0,9,2): print(i)
我们自定义了一个Range类用来模拟range迭代器,运行后发现可以正常运行,当然如果想要百分百模拟我们还有一些地方需要完善
关于__del__:
这个函数有点特殊,我们还是直接说说它的原理吧。在类中定义好__del__后,它会在被python解释器的垃圾回收机制,在类没有被任何调用或者被对象指向的时候,就会被python垃圾回收机制清理来节约内存,这个时候__del__内部的代码就会执行我们来试试
import time class Foo(object): def __del__(self): print('我要被销毁了') a=Foo()
我们发现在程序运行结束后就会执行__del__代码,可能这样看的还不是很清楚,我们再来看看
import time class Foo(object): def __del__(self): print('我要被销毁了') a=Foo() del a time.sleep(5)
我们导入了一个time模块在程序运行结束之前先睡5秒,睡5秒之前先把对象a给删除,我们发现不用等程序结束运行就会执行__del__代码,如果你不信还可以先把del a给注销试试
上下文管理协议__enter__和__exit__:
我们打开文件读取和写入的时候会用上with这个语句,它会在文件读取完毕的时候自动关闭文件,但你有想过这是如何实现的吗?
我们来看看他们的定义和执行顺序
class Foo(object): def __enter__(self): print('from enter') def __exit__(self, exc_type, exc_val, exc_tb): print('from exit') print('exc_type',exc_type) print('exc_val',exc_val) print('exc_tb',exc_tb) with Foo(): print('Foo')
发现会优先执行enter方法,最后执行exit方法,他们到底是什么呢
class Foo(object): def __enter__(self): print('from enter') return 23233 def __exit__(self, exc_type, exc_val, exc_tb): print('from exit') print('exc_type',exc_type) print('exc_val',exc_val) print('exc_tb',exc_tb) with Foo()as a: print(a) print('Foo') raise TypeError('类型错误')
发现enter的作用可以用来返回值后被as后面的变量名给接受值,而exit的那几个参数可以用于接受错误信息
,当在exit中加入return Ture后发现会忽略这些错误。
知道他们的用处后我们来模拟一个用类自定义的日志文件写入的open函数
import time class Open: def __init__(self,filepath,m='r',encoding='utf8'): self.io=open(filepath,mode=m,encoding=encoding) self.filepath=filepath self.mode=m self.encoding=encoding def write(self,line): t=time.strftime('%y-%m-%d %x') self.io.write('%s %s'%(t,line)) def __getattr__(self,item): return getattr(self.io,item) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.io.close() with Open('001.txt','w')as f: f.write('xxxxxx') f.seek(0) with Open('001.txt','r')as r: print(r.read())
完美,还有比这更完美的了吗
元类
什么是元类,通过__dict__发现其实类也是由一个字典组成的,既然类是由字典组成的我们能不能自定义一个类呢?
name='cris' def run(): print('runing') def add(): print('add') cls=type('func',(object),{'run':run,'add':add})
我们可以通过这种定义元类的方式来定义一个cls类,来试试能否正常调用
name='cris' def run(): print('runing') def add(): print('add') cls=type('func',(object,),{'run':run,'add':add}) cls.add() cls.run() print(cls.__name__)
type有三个参数,第一个是类名,第二个是基础关系,第三个是函数字典
通过type我们还可以定制自己的元类:
元类起始就是类的类,可以用来控制类的行为,如果一个父类继承元类,二子类以metaclass方式继承父类,那么就可以在父类中控制类的行为,比如我们限制子类必须写doc文档
class Foo(type): def __init__(self,cls_name,base,dict): for key in dict: if not callable(dict[key]):continue if not dict[key].__doc__: raise TypeError('没有写函数文档这是不允许的') class Foo1(metaclass=Foo): def __init__(self,name): self.name=name def run(self): print('thin is run func') a=Foo1('cris')
这个时候我们在子类中没有写函数文档就会抛出异
实际上在我们实例化一个类的对象的时候就是在调用元类的call方法,我只是知道有这么个操作具体原理我也不明
class Foo(type): def __init__(self,cls_name,base,dict): pass def __call__(self, *args, **kwargs): obj=self.__new__(self) self.__init__(obj, *args, **kwargs) return obj class Foo1(metaclass=Foo): def __init__(self,name): self.name=name def run(self): print('thin is run func') a=Foo1('cris') a.name