关于__setitem__,__getitem__,delitem__以及__slots__,迭代器原理,上下文管理协议还有元类

关于__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
原文地址:https://www.cnblogs.com/crischou/p/6763799.html