正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:
class Student(object):
pass
s = Student()
s.name = ‘Michael‘ # 动态给实例绑定一个属性
s.name
‘Michael‘
还可以尝试给实例绑定一个方法:
# 定义一个函数作为实例方法
def set_age(self, age):
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
s.set_age(25) # 调用实例方法
s.age # 测试结果
25
但是,给一个实例绑定的方法,对另一个实例是不起作用的:
s2 = Student() # 创建新的实例
s2.set_age(25) # 尝试调用方法
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-8-1065b807d51f> in <module>() 1 s2 = Student() # 创建新的实例 ----> 2s2.set_age(25) # 尝试调用方法 AttributeError: ‘Student‘ object has no attribute ‘set_age‘
为了给所有实例都绑定方法,可以给class绑定方法:
def set_score(self, score):
self.score = score
Student.set_score = MethodType(set_score, Student)
s.set_score(100)
s.score
100
s2.set_score(99)
s2.score
99
通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
但是,如果我们想要限制class的属性怎么办?比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class能添加的属性:
class Student(object):
__slots__ = [‘name‘, ‘age‘] # 用list定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = ‘Michael‘ # 绑定属性‘name‘
s.name
‘Michael‘
s.age = 25 # 绑定属性‘age‘
s.age
25
s.score = 99 # 绑定属性‘score‘
s.score
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-15-933abf971f18> in <module>() ----> 1s.score = 99 # 绑定属性‘score‘ 2 s.score AttributeError: ‘Student‘ object has no attribute ‘score‘
由于‘score‘没有被放到slots中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
使用slots要注意,slots定义的属性仅对当前类起作用,对继承的子类是不起作用的:
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score = 9999
g.score
9999
class GraduateStudent2(Student):
__slots__ = [‘sex‘]
g = GraduateStudent2()
g.score = 9999
g.score
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-19-34f58451d863> in <module>() 4 5 g = GraduateStudent2() ----> 6g.score = 9999 7 g.score AttributeError: ‘GraduateStudent2‘ object has no attribute ‘score‘
python中的new-style class要求继承Python中的一个内建类型, 一般继承object,也可以继承list或者dict等其他的内建类型。 在python新式类中,可以定义一个变量slots,它的作用是阻止在实例化类时为实例分配dict, 默认情况下每个类都会有一个dict,通过dict访问,这个dict维护了这个实例的所有属性,举例如下:
class base(object):
var = 9 #类变量
def __init__(self):
pass
b = base()
b.__dict__
{}
b.x = 2 # 添加实例变量
b.x
2
b.__dict__
{‘x‘: 2}
可见:实例的dict只保持实例的变量,对于类的属性是不保存的,类的属性包括变量和函数。 由于每次实例化一个类都要分配一个新的dict,因此存在空间的浪费,因此有了slots。 slots是一个元组,包括了当前能访问到的属性。 当定义了slots后,slots中定义的变量变成了类的描述符,相当于java,c++中的成员变量声明, 类的实例只能拥有slots中定义的变量,不能再增加新的变量。注意:定义了slots后,就不再有dict。如下:
正如上面所说的,默认情况下, Python的新式类和经典类的实例都有一个 dict来存储实例的属性。这在一般情况下还不错,而且非常灵活, 乃至在程序中可以 随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。 当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在 新式类中定义一个slots属性。 slots声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。 【使用memory_profiler模块,memory_profiler模块是在逐行的基础上,测量代码的内存使用率。尽管如此,它可能使得你的代码运行的更慢。使用装饰器 @ profile来标记哪个函数被跟踪。】
from memory_profiler import profile
class A(object): #没有定义__slots__属性
def __init__(self, x):
self.x = x
@profile
def main():
f = [A(523825) for i in range(100000)]
if __name__ == ‘__main__‘:
main()
ERROR: Could not find file <ipython-input-25-dd80325e9c87> NOTE: %mprun can only be used on functions defined in physical files, and not in the IPython environment.
第2列表示该行执行后Python解释器的内存使用情况, 第3列表示该行代码执行前后的内存变化。 在没有定义slots属性的情况下,该代码共使用了20.8MiB内存。 从结果可以看出,内存使用是以MiB为单位衡量的,表示的mebibyte(1MiB = 1.05MB)
from memory_profiler import profile
class A(object): #定义了__slots__属性
__slots__ = (‘x‘)
def __init__(self, x):
self.x = x
@profile
def main():
f = [A(523825) for i in range(100000)]
if __name__ == ‘__main__‘:
main()
ERROR: Could not find file <ipython-input-27-52e485957d48> NOTE: %mprun can only be used on functions defined in physical files, and not in the IPython environment.
第2列表示该行执行后Python解释器的内存使用情况, 第3列表示该行代码执行前后的内存变化。 在没有定义slots属性的情况下,该代码共使用了20.8MiB内存。 从结果可以看出,内存使用是以MiB为单位衡量的,表示的mebibyte(1MiB = 1.05MB)
可以看到,在定义了slots属性的情况下,该代码共使用了6.1MiB内存,比上面的20.8MiB节省了很多内存! 综上所述,在确定了类的属性固定的情况下,可以使用slots来优化内存。
提醒: 不要贸然进行这个优化,把它用在所有地方。这种做法不利于代码维护,而且只有生成数以千计的实例的时候才会有明显效果。
原文:https://www.cnblogs.com/xinmomoyan/p/14626417.html