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__
产生了。
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
网友评论