zoukankan      html  css  js  c++  java
  • 关于__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
  • 相关阅读:
    Bash Shellshock(CVE-2014-6271)破壳漏洞测试
    极客时间-左耳听风-程序员攻略-分布式架构经典图书和论文
    极客时间-左耳听风-程序员攻略-分布式架构入门
    极客时间-左耳听风-程序员攻略-数据库
    极客时间-左耳听风-程序员攻略-Java底层知识
    Hackertarget:一款发现攻击面的工具
    解决tomcat was unable to start within问题
    当我们安装使用时,会出现eclipse启动不了,出现“Java was started but returned exit code=13......”的问题
    Eclipse启动报错:A java runtime Environment(JRE) or java Development……的解决办法
    eclipse svn插件安装方法
  • 原文地址:https://www.cnblogs.com/crischou/p/6763799.html
Copyright © 2011-2022 走看看