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_黑魔法

    Python的类具有很多的特殊方法,__init__()、__new__()、__str__(),还有其他的一些特...

  • Python 黑魔法 --- 描述器(descriptor)

    Python 黑魔法---描述器(descriptor) Python黑魔法,前面已经介绍了两个魔法,装饰器和迭代...

  • python黑魔法

    因为简书的小问题,还有一些在这里,就不再合并了。这里会不断收集有关于python的黑魔法,增加大家的兴趣 使用 l...

  • 【好书推荐】《Python黑魔法指南》-附高清PDF版

    摘要:《Python 黑魔法手册.pdf 》作者(明哥)是一个从事云计算多年的 Python 重度用户,它把自已多...

  • 在 Ubuntu 16.04 LTS 上安装 Python 3.

    最近 Python 3 发布了新版本 Python 3.6.0,好像又加入了不少黑魔法!~ 由于暂时不能使用 ap...

  • Python札记35_多继承

    在上篇札记Python札记34中主要是讨论继承和单继承方面的知识,本篇札记中主要是学习多继承的特点。 一个类是可以...

  • Python 数据分析入门教学

    前言 python可以说是黑魔法了,利用python可以做到很多酷酷的事情,也应了那句 "人生苦短,我用pytho...

  • Python黑魔法大全

    说明:本文描述的所有“黑魔法”都以Python3做示例代码,Python2示例代码请自行转换。本文不探讨Pytho...

  • Python札记52_进程和线程2

    在之前的札记Python札记50_进程和线程1中介绍了进程、线程和子进程以及多进程的相关知识,本札记中重点介绍多线...

  • 一文搞懂什么是Python的metaclass

    Python 有很多黑魔法,为了不分你的心,今天只讲 metaclass。对于 metaclass 这种特性,有两...

网友评论

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

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