zoukankan      html  css  js  c++  java
  • python基础——面向对象进阶下

    python基础——面向对象进阶下

     

     

    1 __setitem__,__getitem,__delitem__

       把对象操作属性模拟成字典的格式

     

       想对比__getattr__(), __setattr__() 和 __deltattr__()这三个通过属性的方式的三个方法

     

     还有__getitem__(), __setitem__() 和 __delitem__()这三个函数, 是通过字典形式来处理属性

     

     字典形式使用中括号的方式获取值

     

     在实现__setitem__()的时候仍然需要使用__dict__来实现增值的设置

    —getitem— 查看并返回一个value的值

    class Foo:
        def __init__(self,name):
            self.name=name
        def __getitem__(self, item): #需要一个返回值
            print('getitem')
            return self.__dict__[item]  #返回一个值value的值
    f=Foo('egon')
    # print(f.name) #值是f.name="egon"
    print(f['name']) #触发getitem运行 把name传给item
    print(f.__dict__) #查看字典里面的值
    

    输出结果为:

    getitem
    egon
    {'name': 'egon'}
    

    __setitem__设置字典里面的值

    class Foo:
        def __init__(self,name):
            self.name=name
    
        def __setitem__(self, key, value):
            print('setitem')
            self.__dict__[key]=value
    
    f=Foo("egon")
    f["age"]=18 #触发 setitem 并打印setitem 并没有执行下面的值
    print(f.__dict__) #查看字典里面的值
    

    输出结果为:

    setitem
    {'name': 'egon', 'age': 18}
    

      

     __delitem__ 删除字典里面的值

    class Foo:
        def __init__(self,name):
            self.name=name
        def __delitem__(self, key):
            print('delitem')
            self.__dict__.pop(key)
    
    f=Foo('egon')
    # del f['age'] #del.f.age
    print(f.__dict__) #删除后打印一下
    

    输出结果为:

    {'name': 'egon'}
    

    2 _slots_

    1.__slots__是什么: 

    是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

    2.引子:

    使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

    3.为何使用__slots__:

    字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__

    当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个.字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

    4.注意事项:

    __slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。

    关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

     

    slots_不会产生一个名称空间,省内存,属性只能设置_slots_规定的属性

    class People:
        _slots_=["name","age"] #可以加值
    p=People()
    p.name="karina"   #对象没有自己的字典也就意味着没有自己的名称空间
    p.age="18"
    print(p.name,p.age)
    

    输出结果为:

    karina 18
    

    3 _iter_ _next_ 实现迭代器协议

     可迭代对象是有方法__iter__()

       迭代器是有方法__next__()

       因而可以自己构建一个类, 使得它的对象, 既是一个可迭代对象, 也是一个迭代器 

    例1

    from collections import Iterable,Iterator #导入模块 判断是不是可迭代对象
    class Foo:
        def __init__(self,start):
            self.start=start
    
        def __iter__(self):
            return self
    
        def __next__(self):
            return 'asb'
    
    f=Foo(0)
    print(next(f)) #触发_next_  f._next_()
    for i in f: #res=f._iter_()  #next(res) 
        print(i) 
    *目前这样的情况下会进入死循环
    # print(isinstance(f,Iterable)) #判断f是不是可迭代对象
    # print(isinstance(f,Iterator)) #判断f是不是迭代器
    

    抛一个异常

    # from collections import Iterable,Iterator #导入模块 判断是不是可迭代对象
    # class Foo:
    #     def __init__(self,start):
    #         self.start=start
    #
    #     def __iter__(self):
    #         return self
    #
    #     def __next__(self):
    #         if self.start>10:  #抛一个异常并 做一个判断
    #             raise StopIteration
    #         n=self.start #设定初始值 从0开始
    #         self.start+=1 #每次自加一次
    #         # return n
    # f=Foo(0)
    # print(next(f))
    # print(next(f))
    # print(next(f))
    # print(next(f))
    # for i in f:
    #     print(i)
    

      

    列2:模拟range功能 

    class Range:
        def __init__(self,start,end):
            self.start=start
            self.end=end
    
        def __iter__(self):
            return self
        def __next__(self):
            if self.start>self.end:
                raise StopIteration
            n=self.start
            self.start+=1
            return n
    r=Range(0,2)
    for i in r :
        print(i)
    

    输出结果为:

    0
    1
    2
    

      

    4 _doc_ 打印注释信息 

    在类中最开始定义的光秃秃的字符串是类的文档信息

    查看该文档信息可以使用__doc__来获取

    在函数中的第一个光秃秃的字符串也是, 通过函数名来调用__doc__即可获得

    另外__doc__无法被继承

    class Foo:
        "我是描述信息"
        pass
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类

    5 __module__

      __module__和__class__是两个特殊属性

      可以通过对象点的方式获取

      __module__是获取对象所在模块的名字. __class__是获取对象所在的类

      具体代码如下 

    import time
    print(time.time.__module__)
    print(time.time.__class__)
    # time
    # <class 'builtin_function_or_method'="">
    </class>
    

      

    6 __del__ 析构函数

    析构方法,当对象在内存中被释放时,自动触发执行。

    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    import time
    class Open:
        def __init__(self,filepath,mode='r',encode='utf8'):
            self.f=open(filepath,mode=mode,encoding=encode)
    
        def write(self):
            pass
    
        def __getattr__(self, item):
            return getattr(self,item)
    
        def __del__(self): #析构函数del
            print('---->del')
            self.f.close() #已删除就关闭文件 做清理操作
    
    f=Open('a.txt','r')
    del f #删除时 去执行__del__
    time.sleep(100)
    

    输出结果为:

    ---->del

    7 __enter__和__exit__

    我们知道在操作文件对象的时候可以这么写

    1 with open('a.txt') as f:
    2   '代码块'
    

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    class Foo:
        def __enter__(self):
            print('enter')
            return "111"
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('exit')
            print('exc_type',exc_type)  #打印类型
            print('exc_val',exc_val)    #打印值
            print('exc_tb',exc_tb)      #打印追踪信息
            return True #返回一个不为假的布尔值,代表已经出来过异常,程序不会蹦掉
    
    # with Foo(): #res=Foo()._enter_
    #     pass
    
    with Foo() as obj: #res=Foo()._enter_ #obj=res  #with 触发_enter 然后执行打印拿回一个返回值
        print('with foo 的自代码块',obj)#再执行with的自代码块,打印的值为None
                                        #因为obj目前是没有值,如果有返回值,obj就会返回值
        raise NameError("名字属性定义") #主动抛出异常 触发 exit方法  在with的自代码中抛出异常,
                                        # 意味着with自代码块执行完,后面不管有没有代码都不执行
    

    输入结果为:

    enter
    exit
    exc_type None
    exc_val None
    exc_tb None
    enter
    with foo 的自代码块 111
    exit
    exc_type <class 'NameError'>
    exc_val 名字属性定义
    exc_tb <traceback object at 0x02558058>
    

    用途或者说好处:

    1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

    2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。 

    8 __call__

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class People:
        def __init__(self,name):
            self.name=name
    
        def __call__(self, *args, **kwargs):
            print("call")
    p=People("egon")
    p() #属性直接加括号运行
    

    输出结果为:

    call
    

    9 元类

    1、所有的对象都是由类产生的,类又是有type产生的

     type--->类----->对象

     type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程

     创建一个类两方面要素 一个是类的名字,一个是名称空间定制的属性
     在python3里,所有的类都是新式类,都有继承object
     type()手动创建一个类三点:#类名 字符串形式 #类继承关系 元祖形式 #类的属性名称空间 字典类型

    def run(self):
        print('%s is runing'%self.name)
    
    class_name='Bar' #类名
    bases=(object,)    #类继承关系
    class_dic={
        "x":1,        #类的属性名称空间
        "run":run
    }
    Bar=type(class_name,bases,class_dic)
    print(Bar)

    输出结果为:

    <class '__main__.Bar'>
    
    class_name='Spam'
    bases=(object,)
    class_dic={
        "name":"karina",
        "age":18
    }
    Spam=type(class_name,bases,class_dic)
    print(Spam)
    print(Spam.__dict__)
    

    输出结果为:

    <class '__main__.Spam'>
    {'name': 'karina', 'age': 18, '__module__': '__main__', '__dict__': 
    <attribute '__dict__' of 'Spam' objects>, '__weakref__': <attribute '__weakref__' of 'Spam' objects>, '__doc__': None}

      

    2、自定义元类

    可以写一个元类Mateclass, 它需要继承自type类

      原来的类需要关联该元类, 也就是在继承中有 metaclass=元类名字

      此时执行元类就可以生成一个对象, 也就是创建的这个类

      基于此, 就是元类中的__init__()方法创建的 类对象, 所以新加的功能只需在__init__()方法中就行

      元类的__init__()有额外三个参数, 分别是类名, 类基类, 类属性字典

      实现检查__doc__的代码如下

    class MyMetaclass(type):
        def __init__(self, class_name, class_bases, class_dic):
            for key, value in class_dic.items():
                if callable(value) and not value.__doc__:
                    raise Exception("{}方法内必须写入注释..".format(key))
     
    class Foo(metaclass=MyMetaclass):
        x = 1
        def __init__(self, name):
            self.name = name
        def run(self):
            'run function'
            print('running')
        def go(self):
            print('going') 

    元类可以创建类的行为:

    type
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            pass
        def __call__(self, *args, **kwargs):
            # print(self)
            obj=self.__new__(self) #产生空对象
            self.__init__(obj,*args,**kwargs) #obj.name='egon'
            return obj
    
    class Foo(metaclass=Mymeta):#指定元类等于Mymeta
        x=1
        def __init__(self,name):
            self.name=name  #obj.name='egon  不能有返回值
        def run(self):
            print("running")
    f=Foo('egon')
    print(f)
    
  • 相关阅读:
    python 连接sql server 解决中文乱码 及配置使用 web 服务使用
    Android调用.net的webservice服务器接收参数为空的情况
    好题推荐
    算法中一些trick和细节
    洛谷P2181 对角线
    新的开始
    文化课倒计时80天
    Electron-vue实现后台多进程(三. 自动化测试篇)
    工作感受月记202107月
    工作感受月记202106月
  • 原文地址:https://www.cnblogs.com/niejinmei/p/6765178.html
Copyright © 2011-2022 走看看