下面就为大家详细讲解“Python类中的魔法方法之__slots__原理解析”。
一、背景介绍
在Python中,每个对象实例都会占用一定的空间。同时,我们经常会遇到一些类需要创建大量对象实例的场景,例如在Web开发领域中,经常需要创建大量的实例(如请求对象)。
在这种场景下,如果每次创建实例的时候都需要动态地添加属性,那么就会比较耗费时间和内存,因为Python需要为每个实例额外的空间来储存这些属性。而Python类中的__slots__
魔法方法可以帮助我们优化这种情况。
二、__slots__
方法的作用
在Python中每个实例都有一个__dict__
属性,用来存储实例的属性。__slots__
方法允许我们事先声明某些属性名称,来告诉Python不要再为这些属性分配内存和创建字典了,从而优化空间和执行效率。
举个例子,如果我们定义这样一个类:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
那么每次创建一个这个类的实例,都会动态地为 name
,age
新建一个对象属性的字典__dict__,就算没有实际赋值的__dict__,也会存在对象的空间中。这里就又有了一个难摆脱的问题,如果我们有一些非常非常多的实例对象呢?显然这会造成很大的内存浪费,增加了Python GC(垃圾回收机制)的压力。
而如果使用__slots__
方法,可以声明这些属性,从而让Python不再动态分配内存和创建实例的属性字典__dict__。__slots__
方法使用一个元组来指定这些属性,例如:
class Person:
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
这里我们定义了一个Person
类,声明了name
和age
属性。此时,调用类的__slots__
后,创建的实例就不再具有__dict__
属性,而是会在类中直接保存这两个属性。这样,即使我们创建很多实例,也不会出现空间浪费的问题了。
除了减少内存浪费,使用__slots__
还可以提高查询和赋值的速度,因为Python不再使用字典来维护对象属性。不过需要注意的是,Python中的__slots__
只对当前类的实例起作用,对继承的子类不会有影响。
三、一个简单的应用实例
为了更好地理解__slots__
,我们来看一个具体的例子。下面我们定义一个foo
类,含有a
,b
,c
三个属性。我们可以使用__dict__
查看实例对象的内存占用情况。
class foo():
def __init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
f=foo(1,2,3)
print(f.__dict__)
输出为:
{'a': 1, 'b': 2, 'c': 3}
可以看到,这个对象占用了可观的内存:同时含有三个属性及其键。
接下来,我们改变这个类的定义方式,添加__slots__
属性。
class foo():
__slots__=('a','b','c')
def __init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
f=foo(1,2,3)
print(f.__slots__)
输出为:
('a', 'b', 'c')
可以看到,输出内容已经不再是一个字典,而是一个元组,元素为a
,b
,c
3个属性名。 另外,使用__slots__
定义的类同样可以赋值和属性提取 :
print( f.a,f.b,f.c )
f.a,f.b,f.c=4,5,6
print( f.a,f.b,f.c )
输出为:
1 2 3
4 5 6
从输出结果和我们预料的相符: 使用了__slots__
类型的类占用的内存更少,同时还能够正常使用赋值和属性提取两个基本操作。
四、Tips
- 所有新式的类都会默认继承object,所以不需要在类定义时继承于object
__slots__
在类继承中只对自身起作用,不会对父类或者子类产生影响- 利用
__slots__
,可以限制动态地添加属性(__dict__
)
到此结束,希望对大家有所帮助。