__slots__

在python中,我们可以在实例化对象后绑定对象的方法或属性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Employer:
    pass


e = Employer()
e.name = "alfons"
print("name is -> ", e.name)

e.salary = 1000
print("salary is -> ", e.salary)

# output
name is ->  alfons
salary is ->  1000

__slots__ 在类中声明,类型为 list 或 tuple,表示的是 对象在实例化后能够绑定的属性的限制,也就是说,之后绑定的属性名被限定在 __slots__ 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

class Employer:
    __slots__ = ["name"]
    pass


e = Employer()
e.name = "alfons"
print("name is -> ", e.name)

e.salary = 1000
print("salary is -> ", e.salary)

# output
name is ->  alfons
Traceback (most recent call last):
  File "/mnt/3a4a987a-73ee-49eb-bc4b-b34ee82abd09/PythonCode/Python/MagicMethod/__slots__.py", line 19, in <module>
    e.salary = 1000
AttributeError: 'Employer' object has no attribute 'salary'

如果子类继承了父类,并且父类中声明了 __slots__ ,子类的情况会出现两种:

  • 子类中未声明,那么子类不会继承父类中的 __slots__
  • 子类中也声明了 __slots__,那么就会继承父类的 __slots__

(有点绕啊)

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 1 子类不声明
class Human:
    __slots__ = ["name"]


class Employer(Human):
    pass


e = Employer()
e.name = "alfons"
print("name is -> ", e.name)

e.salary = 1000
print("salary is -> ", e.salary)

# output
name is ->  alfons
salary is ->  1000

# 2 子类声明
class Human:
    __slots__ = ["name"]


class Employer(Human):
    __slots__ = ["salary"]


e = Employer()
e.name = "alfons"
print("name is -> ", e.name)

e.salary = 1000
print("salary is -> ", e.salary)

e.sex = "male"
print("sex is -> ", e.sex)

# ouput
name is ->  alfons
salary is ->  1000you hua
Traceback (most recent call last):
  File "/mnt/3a4a987a-73ee-49eb-bc4b-b34ee82abd09/PythonCode/Python/MagicMethod/__slots__.py", line 25, in <module>
    e.sex = "male"
AttributeError: 'Employer' object has no attribute 'sex'

另外,如果 __slots__ 中包含了 __dict__,则 __slots__ 将不起作用!!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Human:
    __slots__ = ["name"]


class Employer(Human):
    __slots__ = ["__dict__", "salary"]


e = Employer()
e.name = "alfons"
print("name is -> ", e.name)

e.salary = 1000
print("salary is -> ", e.salary)

e.sex = "male"
print("sex is -> ", e.sex)

# output
name is ->  alfons
salary is ->  1000
sex is ->  male

降低内存使用

其实, __slots__ 最主要的作用还是 降低设备的内存使用,它限定了实例中属性的范围。

例子可以参考:Saving 9 GB of RAM with Python’s slots

(不过我做实验却得不到差异,不知道是不是现在的版本已经做了相关的优化。至少Pypy是已经优化的了。)

__all__

在标准库模块中,经常会看见 __all__ 列表,里面会包含很多字符串,字符串的名称与该模块的导入函数有许多相同的名称。如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# struct.py
__all__ = [
    # Functions
    'calcsize', 'pack', 'pack_into', 'unpack', 'unpack_from',
    'iter_unpack',

    # Classes
    'Struct',

    # Exceptions
    'error'
    ]

from _struct import *
from _struct import _clearcache
from _struct import __doc__

__all__ 表示的是程序中在 import * 后能够直接使用的集合。例如下面:

1
2
3
4
5
6
7
from struct import *

p = pack("I2s", 2, b"he")
print(p)

u_p = unpack("I2s", p)
print(u_p)

struct 模块中 __all__ 中包含了 pack() 和 unpack() 函数,因此,在程序中可以直接使用。

不过我不太喜欢这种用法,相比于上面直接使用函数,更喜欢加上所属的模块,明了一些。

1
2
3
4
5
6
7
import struct

p_2 = struct.pack("I2s", 2, b"he")
print(p_2)

u_p_2 = struct.unpack("I2s", p_2)
print(u_p_2)