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
Michael
25
80
 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__时需要注意的几点:

  1. 当一个类的父类没有定义__slots__属性,父类中的__dict__属性总是可以访问到的,所以只在子类中定义__slots__属性,而不在父类中定义是没有意义的。
  2. 如果定义了__slots__属性,还是想在之后添加新的变量,就需要把__dict__字符串添加到__slots__的元组里。
  3. 定义了__slots__属性,还会消失的一个属性是__weakref__,这样就不支持实例的weak reference,如果还是想用这个功能,同样,可以把__weakref__字符串添加到元组里。
  4. __slots__功能是通过descriptor实现的,会为每一个变量创建一个descriptor。
  5. __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包含实现。


转载请注明本网址。