python通过__slots__限制类实例动态的增加属性、方法

本文介绍了Python中__slots__的使用方法及其限制条件。详细解释了如何利用__slots__来约束类实例能够绑定的属性,并探讨了其对类继承的影响。此外,还讨论了__slots__对于动态添加方法的限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

__slots__实例详解

python是动态语言,在创建了一个class实例后,我们可以给该实例绑定任何属性和方法。如下所示:

一、python的动态性

>>> class Student(object):
...     pass
... 
>>> s = Student()
>>> s.name = 'stemon'
>>> print s.name
stemon
>>> def set_score(self, score):
...     self.score = score
... 
>>> from types import MethodType
>>> s.set_score = MethodType(set_score, s, Student)
>>> s.set_score(100)
>>> print s.score
100

这里的动态绑定是针对类实例的,给一个实例绑定的属性或者方法,对另一个实例是不起作用的:

>>> s2 = Student()
>>> s2.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'name'
>>> s2.set_score(100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'set_score'

为了给所有的实例都动态绑定该方法,可以给class动态绑定这个方法:

>>> Student.set_score = MethodType(set_score, None, Student)
>>> s2.set_score(100)
>>> print s2.score
100
>>> s.set_score(99)
>>> print s.score
99

通常情况下,类似set_score 这样的方法直接定义在class中,但是这种动态绑定允许我们在程序运行的过程中给class增加功能。

二、__slots__的使用

如果我们想要限制python中class的属性,python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class的实例能添加的属性,比如只能添加name和age两个属性:

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...

然后测试一下:

>>> s.name = 'stemon'
>>> s.age = 25
>>> s.score = 99
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

再动态添加一个方法试试:

>>> s.set_score = MethodType(set_score, s, Student)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'set_score'

可见,定义一个__slots__之后,向类实例中添加任何不在__slots__值范围之内的属性或者方法都是不可以的。


三、__slots__的继承性

如果子类没有定义__slots__,那么父类__slots__定义的属性仅对当前类起作用,对继承的子类不起作用。
如果子类也定义了__slots__,那么子类允许定义的属性就是自身的__slots__加上父类的__slots__。
再回到Student的定义:

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...

不带__slots__的子类的定义:

>>> class GraduateStudent(Student):
...     pass
...
>>> g = GraduateStudent()
>>> g.score = 9999

可以看到,不受父类的__slots__的限制

带__slots__的子类的定义:

>>> class GraduateStudent(Student):
...     __slots__ = ('score')
... 
>>> g = GraduateStudent()
>>> g.name = 'stemon'
>>> g.age = 26
>>> g.score = 9999
>>> g.height = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'GraduateStudent' object has no attribute 'height'

可见子类如果定义了__slots__便会继承父类的__slots__;如果子类没有定义__slots__,便不会受到父类__slots__的限制。

注意一点__slots__限制的仅仅是类的实例的属性或者方法的动态添加,类本身的属性的添加不受__slots__的限制:

>>> GraduateStudent.width = 100
>>> GraduateStudent.height = 100
>>> print g.height
100
>>> print g.width
100

四、__slots__动态添加方法的疑问

补充分析:看下面的一个列子

>>> class Student(object):
...     __slots__ = ('name', 'age', 'set_score')
... 
>>> s = Student()
>>> s.name = 'stemon'
>>> s.age = 25
>>> def set_score(self, score):
...     self.score = score
... 

>>> from types import MethodType
>>> s.set_score = MethodType(set_score, s, Student)
>>> s.set_score(100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in set_score
AttributeError: 'Student' object has no attribute 'score'

可以看到,如果在__slots__中定义了方法的名字就可以动态添加方法,但是如果该方法中又定义了其他的不在__slots__中的属性,那么会在该方法调用的时候产生异常。

同时注意,__slots__限制的是类的实例的动态属性或者方法的添加,在类的__init__ 等方法中定义的属性都是动态的,都受到__slots__的限制。在__init__ 等方法的执行过程中,定义的属性跟终端通过实例增加属性是一样的,都是动态的。

在终端通过实例增加方法,受到__slots__的限制,但是在类的声明中通过def实现的方法是不受__slots__限制的,因为在类的声明中实现的方法是静态的。

### Python `__slots__` 的用法和作用 #### 定义与基本概念 在Python定义中,可以通过设置特殊属性`__slots__`来限定实例可以拥有的属性名称列表。这不仅有助于减少内存占用,还能提高属性访问速度。 当声明了一个包含`__slots__`变量的之后,该创建的对象将无法动态添加新的成员变量[^1]。 ```python class MyClassWithSlots: __slots__ = ['name', 'identifier'] def __init__(self, name, identifier): self.name = name self.identifier = identifier ``` 上述代码展示了如何通过指定`__slots__`仅允许对象拥有特定的名字(name)和标识符(identifier),任何尝试给此类实例增加其他未列于`__slots__`中的属性都会引发异常。 对于不使用`__slots__`的情况: ```python class RegularClassWithoutSlots: def __init__(self, value): self.value = value instance_without_slots = RegularClassWithoutSlots(42) instance_without_slots.new_attribute = "This works fine" print(instance_without_slots.new_attribute) # 输出: This works fine ``` 而如果试图向具有`__slots__`约束的类实例分配额属性,则会抛出错误: ```python my_instance_with_slots = MyClassWithSlots('example_name', 123456) try: my_instance_with_slots.unexpected_attr = True except AttributeError as e: print(e) # 输出似于:'MyClassWithSlots' object has no attribute 'unexpected_attr' ``` #### 使用场景及其优势 - **节省空间**: 对于大量小型类实例的应用程序来说非常重要;因为每个实例不再需要字典(dict)存储其状态,而是直接利用固定大小的数据结构。 - **性能提升**: 访问受控字段的速度更快,由于这些字段的位置是预先确定好的,在某些情况下能带来显著效率增益。 - **防止误操作**: 可以有效阻止开发者无意间修改不该被改变的状态信息,增强了代码的安全性和可维护性。 需要注意的是,一旦启用了`__slots__`特性,默认情况下子不会继承父所设定的槽位集合。因此,若希望保持此功能需显式重申或扩展之。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值