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__
{}
>>a.age = 12
>>a.__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")
>>dir(Spring)
['__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
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
you use getattr
>>a.x = 5
you use setattr
image.png
class NewRectangle:
def __init__(self):
self.width = 0
self.length = 0
def __setattr__(self, name, value):
if name == "size":
self.width, self.length = value
else:
self.__dict__[name] = value
def __getattr__(self,name):
if name == "size":
return self.width, self.length
else:
raise AttributeError
if __name__ == "__main__":
r = NewRectangle()
r.width = 3
r.length = 4
print(r.size)
r.size = 30, 40
print(r.width)
print(r.length)
结果:
(3, 4)
30
40