Python札记40_黑魔法

作者: 皮皮大 | 来源:发表于2019-07-11 20:10 被阅读1次

    Python的类具有很多的特殊方法,__init__()__new__()__str__(),还有其他的一些特殊用法,称之为“黑魔法”

    • 优化内存
      • 通过__slots__属性进行内存优化
      • 增加属性只能通过类属性来实现
      • 加快属性加载速度
    • 属性拦截

    优化内存

    每个类都有一个特殊的属性:.__dict__,它包含了当前类的类属性。

    class Foo:
        name = "xiaoming"
    
    >>Foo.__dict__
    

    结果:

    mappingproxy({'__module__': '__main__',
                  'name': 'xiaoming',
                  '__dict__': <attribute '__dict__' of 'Foo' objects>,
                  '__weakref__': <attribute '__weakref__' of 'Foo' objects>,
                  '__doc__': None})
    
    >>Foo.name   # 直接通过类调用实例
    'xiaoming'
    
    >>a = Foo()  # 调永类,建立实例
    >>a.__dict__   # 查看实例的__dict__属性,为空
    {}
    >>a.age = 12   # 创建实例的属性
    >>a.__dict__   # dict属性不再是空的
    {'age': 12}
    
    >>dir(Foo)  # 查看类的所有属性
    
    ['__class__',
     '__delattr__',
     '__dict__',
     '__dir__',   # 重点关注
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__gt__',
     '__hash__',
     '__init__',
     '__init_subclass__',
     '__le__',
     '__lt__',
     '__module__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__setattr__',
     '__sizeof__',
     '__str__',
     '__subclasshook__',
     '__weakref__',
     'name']
    

    小结

    • 带有双下滑线的__dict__都是特殊属性
    • 每个类都有__dict__属性,表示类的所有类属性
    • 建立的实例也有__dict__属性,刚开始是空的
    • 说明类和实例的__dict__属性是有区别的
    • 一个类可以创建无数个实例,类是实例的工厂
    • 每创建一个实例就会产生一个新的__dict__,当实例多的时候,很占内存,特殊属性__slots__产生了。
    image.png
    class Spring:
        __slots__ = ("tree", "flower")   # 创建 __slots__属性
        
    >>dir(Spring)   # __dict__属性消失
    ['__class__',
     '__delattr__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__gt__',
     '__hash__',
     '__init__',
     '__init_subclass__',
     '__le__',
     '__lt__',
     '__module__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__setattr__',
     '__sizeof__',
     '__slots__',
     '__str__',
     '__subclasshook__',
     'flower',  # 多出来的两个属性,属于普通属性
     'tree']
    
    >>Spring.__slots__    # 类属性只有两个
    ('tree', 'flower')
    
    >>Spring.tree = "zhangshu"
    >>Spring.tree
    'zhangshu'
    >>Spring.tree = "yinxingshu"   # 通过类属性进行赋值和修改
    >>Spring.tree
    'yinxingshu'
    

    实例化之后

    >>t = Spring()
    >>t.__slots__  # 也是两个属性值
    ('tree', 'flower')
    
    >>f = Spring()
    >>f.__slots__
    ('tree', 'flower')
    
    >>id(t.__slots__)
    >>id(f.__slots__)
    1554379682696  # 不同实例的__slots__在内存中是同一个
    
    image.png

    小结

    • 实例的__slots__与类的完全一样,和__dict__不同
    • 不同实例的__slots__在内存中是同一个
    • 增加实例不增加__slots__属性
    >>t.tree= "guangyulan"   # 不能通过实例修改属性的值
    
    AttributeError                            Traceback (most recent call last)
    <ipython-input-21-bf9f758ef89e> in <module>
    ----> 1 t.tree= "guangyulan"
    
    AttributeError: 'Spring' object attribute 'tree' is read-only  # 类属性是只读的
    
    • 对于实例属性,类的静态数据是只读的,不能修改
    • 只能通过类属性才可以修改
    • 尚未赋值的属性,能够通过实例进行赋值,看下面的例子:
    >>t.flower = "juhua"
    >>t.flower
    'juhua'
    
    >>Spring.flower  # 实例属性的值并没有传到类属性中
    <member 'flower' of 'Spring' objects>
    
    >>Spring.flower = "zhizhihua"   # 通过类属性赋值会影响实例属性的值:说明类属性对实例属性具有决定作用
    >>t.flower
    'zhizhihua'
    
    • 对于实例而言,通过类定义的属性都是只读的
    • 类的静态数据不能修改,只能通过类属性才可以进行修改
    • 类属性对实例具有决定作用

    属性拦截

    当访问某个类或者实例属性的时候,如果不存在,则会报错出现异常,出现异常就要处理。Python中具有属性拦截功能的方法(特殊方法,带上双下划线):

    方法 作用
    setattr(self,name,value) 给某个name赋值,进行调用
    getattr(self,name) 访问某个name,如果它不存在,调用此方法
    getattribute(self,name) 当name访问时候自动被调用,name存在与否,都要调用
    delattr(self,name) 如果删除name,调用这个方法
    class A:
        def __getattr__(self,name):
            print("you use getattr")
            
        def __setattr__(self,name,value):
            print("you use setattr")
            self.__dict__[name] = value
    
    >>a = A()
    >>a.x   # a没有x属性,调用getattr方法
    you use getattr
    
    >>a.x = 5   # 此时属性x已经保存在__dict__中,可以进行调用
    you use setattr
    
    image.png
    class NewRectangle:
        def __init__(self):  # 初始化函数
            self.width = 0
            self.length = 0
            
        def __setattr__(self, name, value):  # 给name属性赋值的时候进行调用
            if name == "size":      # r.size = 30, 40
                self.width, self.length = value     #  value = (30, 40)
            else:
                self.__dict__[name] = value
                
        def __getattr__(self,name):   # 访问name属性,如果不存在则调用
            if name == "size":
                return self.width, self.length
            else:
                raise AttributeError
                
    if __name__ == "__main__":
        r = NewRectangle()
        # size属性进行访问;不存在,调用__getattr__方法
        r.width = 3   # 两个属性值
        r.length = 4
        print(r.size)    # size属性不存在,调用__getattr__方法;返回的就是上面两个属性值
       # 对size属性进行赋值,调用__setattr__方法
        r.size = 30, 40  
        print(r.width)
        print(r.length)
    
    结果:
    (3, 4)
    30
    40
    

    相关文章

      网友评论

        本文标题:Python札记40_黑魔法

        本文链接:https://www.haomeiwen.com/subject/xzcjkctx.html