object.__dict__
用于存储对象(可写)属性的字典或其他映射对象。每一层的__dict__只存储该层新增的属性。子类不需要重复存储父类中的属性。
关于class中__dict__的理解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
class Friends:
str = 'Friends Class'
def __init__(self):
self.name = 'wuq'
self.gender = 'female'
def say_to_friend(self):
print('hello %s'%self.name)
# 新建类实例f,m
f = Friends()
m = Friends()
# 输出实例f,m的__dict__
print("添加属性前f的值:",f.__dict__)
print("添加属性前m的值:",m.__dict__)
# 输出类Friends的__dict__
print("添加属性前Friends的值:",Friends.__dict__)
# 实例f添加message属性
f.message = 'hello my world'
# 实例f添加fun方法属性
f.fun = lambda :x
# 输出实例f的__dict__
print("添加属性后f的值:",f.__dict__)
# 输出类Friends的__dict__
print("添加属性后Friends的值:",Friends.__dict__)
# 输出实例m的__dict__
print("添加属性后m的值:",m.__dict__)
|
执行以上程序会输出如下结果:
1
2
3
4
|
添加属性前f的值: {'name': 'wuq', 'gender': 'female'} 添加属性前m的值: {'name': 'wuq', 'gender': 'female'} 添加属性前Friends的值: {'say_to_friend': <function Friends.say_to_friend at 0x7fea185471e0>, '__dict__': <attribute '__dict__' of 'Friends' objects>, '__weakref__': <attribute '__weakref__' of 'Friends' objects>, '__doc__': None, '__module__': '__main__', '__init__': <function Friends.__init__ at 0x7fea18547158>, 'str': 'Friends Class'}
添加属性后f的值: {'name': 'wuq', 'message': 'hello my world', 'gender': 'female', 'fun': <function <lambda> at 0x7fea18638bf8>}
添加属性后Friends的值: {'say_to_friend': <function Friends.say_to_friend at 0x7fea185471e0>, '__dict__': <attribute '__dict__' of 'Friends' objects>, '__weakref__': <attribute '__weakref__' of 'Friends' objects>, '__doc__': None, '__module__': '__main__', '__init__': <function Friends.__init__ at 0x7fea18547158>, 'str': 'Friends Class'}
添加属性后m的值: {'name': 'wuq', 'gender': 'female'}
|
由此可见,实例的__dict__仅存了与实例相关的实例属性和方法。类的__dict__则是和实例共享的变量,函数(方法,类属性)。
如果实例没有添加自己独有的属性,那么实例的的__dict__相同,添加了独有的属性,实例的__dict__不相同。这样就使得每个实例的实例属性不会相互影响。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Friends:
str = 'Friends Class'
def __init__(self):
self.name = 'wuq'
self.gender = 'female'
def say_to_friend(self):
print('hello %s'%self.name)
class GirlFriends(Friends):
new_str = 'GirlFriends Class'
def __init__(self,age):
super().__init__(self)
self.age = age
def new_say_to_friend(self):
print('hello %s'%self.name)
#输出类Friends的__dict__
print("父类Friends的值:",Friends.__dict__)
print("子类GirlFriends的值:",GirlFriends.__dict__)
|
执行以上程序会输出如下结果:
1
2
|
父类Friends的值: {'__module__': '__main__', 'say_to_friend': <function Friends.say_to_friend at 0x7f63587a01e0>, '__dict__': <attribute '__dict__' of 'Friends' objects>, 'str': 'Friends Class', '__init__': <function Friends.__init__ at 0x7f63587a0158>, '__weakref__': <attribute '__weakref__' of 'Friends' objects>, '__doc__': None}
子类GirlFriends的值: {'__module__': '__main__','new_say_to_friend': <function GirlFriends.new_say_to_friend at 0x7f63587a02f0>,'new_str': 'GirlFriends Class', '__init__': <function GirlFriends.__init__ at 0x7f63587a0268>,'__doc__': None}
|
注意 子类的__dict__并不包含其父类的属性。子类的__dict__中不包含父类的__dict__。
下面的例子看的更清楚,子类的__dict__中没有父类的__dict__属性。
1
2
3
4
5
6
7
8
9
|
class A:
pass
class B(A):
pass
#输出类Friends的__dict__
print("父类A的值:",A.__dict__)
print("子类B的值:",B.__dict__)
|
执行以上程序会输出如下结果:
1
2
|
父类A的值: {'__doc__': None, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__'}
子类B的值: {'__doc__': None, '__module__': '__main__'}
|
__slots__
我们知道python时动态语言,可以在运行过程中给实例绑定属性和方法。
例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from types import MethodType
class Student(object):
pass
s = Student() # 创建实例s
s.name = 'Michael' # 实例s绑定name属性
print(s.name) # 打印实例s的name属性
#定义方法
def set_age(self, age):
self.age = age
s.set_age = MethodType(set_age,s) #实例绑定方法
s.set_age(25) #调用实例方法
print(s.age) #打印实例s的age属性
stu = Student() #创建实例stu
stu.set_age(50) #调用实例方法,没有绑定set_age,报错
print(stu.age) #实例绑定的方法,对stu实例是不起作用。
|
执行以上程序会输出如下结果:
1
2
3
4
5
6
|
Michael
25
Traceback (most recent call last):
File "/home/main.py", line 20, in <module>
stu.set_age(50) #调用实例方法
AttributeError: 'Student' object has no attribute 'set_age'
|
为了给所有实例都绑定方法,可以给class绑定方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Student(object):
pass
#定义方法
def set_score(self, score):
self.score = score
Student.set_score = set_score
s = Student() # 创建实例s
stu = Student() # 创建实例stu
s.set_score(25)
stu.set_score(50)
print(s.score) #输出25
print(stu.score) #输出50
|
__slots__是一个类变量,__slots__属性可以赋值一个包含类属性名的字符串元组,或者是可迭代变量,或者是一个字符串,只要在类定义的时候,使用__slots__=aTuple来定义该属性就可以了。__slots__变量,用来限制class实例能添加的属性。
1
2
3
4
5
6
7
8
9
|
class Student:
#实例只能绑定age和name属性,绑定其他的会报错
__slots__ = ('name', 'age')
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score'
|
报错如下:
1
2
3
|
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
|
__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Student:
#实例只能绑定age和name属性,绑定其他的会报错
__slots__ = ('name', 'age')
class UniversityStudent(Student):
pass
u = UniversityStudent() # 创建新的实例
u.name = 'Michael' #绑定属性'name'
u.age = 25 #绑定属性'age'
u.score = 80 #子类实例对象可以绑定__slots__限制意外的属性'score'
print(u.name)
print(u.age)
print(u.score)
|
执行以上程序会输出如下结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Student:
#实例只能绑定age和name属性,绑定其他的会报错
__slots__ = ('name', 'age')
class UniversityStudent(Student):
__slots__ = ('gender', 'weight')
u = UniversityStudent() # 创建新的实例
u.name = 'Michael' #绑定父类__slots__限制的属性'name'
u.age = 25 #绑定父类__slots__限制的属性'age'
u.gender = "male" #绑定__slots__限制的属性'gender'
u.weight = "65KG" #绑定__slots__限制的属性'weight'
print(u.name)
print(u.age)
print(u.gender)
print(u.weight)
u.score = 80 #父类和子类的__slots__都没有score属性,会报错
|
执行以上程序会输出如下结果:
1
2
3
4
5
6
7
|
Michael
25
male
65KG
Traceback (most recent call last):
File "/home/main.py", line 19, in <module> u.score = 80 #父类和子类的__slots__都没有score属性,会报错
AttributeError: 'UniversityStudent' object has no attribute 'score'
|
使用时__slots__时需要注意的几点:
- 当一个类的父类没有定义__slots__属性,父类中的__dict__属性总是可以访问到的,所以只在子类中定义__slots__属性,而不在父类中定义是没有意义的。
- 如果定义了__slots__属性,还是想在之后添加新的变量,就需要把__dict__字符串添加到__slots__的元组里。
- 定义了__slots__属性,还会消失的一个属性是__weakref__,这样就不支持实例的weak reference,如果还是想用这个功能,同样,可以把__weakref__字符串添加到元组里。
- __slots__功能是通过descriptor实现的,会为每一个变量创建一个descriptor。
- __slots__的功能只影响定义它的类,因此,子类需要重新定义__slots__才能有它的功能。
.使用__slots__好处:节省内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import sys
class test1:
def __init__(self,name):
self.name = name
class test2:
__slots__ = ["name"]
def __init__(self,name):
self.name = name
a = test1('alex')
b = test2('alex')
#sys模块中的getsizeof方法是获得对象内存占用内存大小。
print(sys.getsizeof(a)) #输出56
print(sys.getsizeof(b)) #输出48
print("没有__slots__ 属性的test1的值: ",test1.__dict__)
print("有__slots__ 属性的test2的值: ",test2.__dict__)
|
执行以上程序会输出如下结果:
1
2
3
4
|
56
48
没有__slots__ 属性的test1的值: {'__dict__': <attribute '__dict__' of 'test1' objects>, '__weakref__': <attribute '__weakref__' of 'test1' objects>, '__doc__': None, '__module__': '__main__', '__init__': <function test1.__init__ at 0x7f2d00724158>}
有__slots__ 属性的test2的值: {'name': <member 'name' of 'test2' objects>, '__doc__': None, '__module__': '__main__', '__slots__': ['name'], '__init__': <function test2.__init__ at 0x7f2d007241e0>}
|
上述代码可以看到,python为对象a分配了56 Byte的内存,而为对象b分配了48 Byte的内存,总共节省了8 Byte的内存空间。通过__dict__的属性值就能看出,含有__slots__属性的类去除了一些属性(例如__slots__,__weakref__)来节省内存。
__dict__属性与__slots__属性关系
通常每一个实例x都会有一个__dict__属性,用来记录实例中所有的属性和方法,也是通过这个字典,可以让实例绑定任意的属性。而__slots__属性作用就是,当类C有比较少的变量,而且拥有__slots__属性时,类C的实例就没有__dict__属性,而是把变量的值存在一个固定的地方。如果试图访问一个__slots__中没有的属性,实例就会报错。这样操作有什么好处呢?__slots__属性虽然令实例失去了绑定任意属性的便利,但是因为每一个实例没有__dict__属性,却能有效节省每一个实例的内存消耗,有利于生成小而精干的实例。
为什么需要这样的设计呢?
在一个实际的企业级应用中,当一个类生成上百万个实例时,即使一个实例节省几十个字节都可以节省一大笔内存,这种情况就值得使用__slots__属性。
设计类的继承关系时,通常,主线都是单一继承下来的。但是,如果需要“混入”额外的功能,通过多重继承就可以实现。这种设计通常称之为MixIn。与传统的类继承有所不同。通常mixin并不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。Mixin类似于java中的interface,interface并不包含实现,而Mixin包含实现。
转载请注明本网址。