使用__slots__

Python 动态属性绑定与 __slots__ 使用教程

1. Python 动态属性绑定

Python 作为动态语言,允许我们在运行时动态地为类和实例添加属性和方法。

1.1 动态绑定实例属性

class Student:
    pass

s = Student()
s.name = 'Michael'  # 动态添加实例属性
print(s.name)  # 输出: Michael

1.2 动态绑定实例方法

def set_age(self, age):
    self.age = age

from types import MethodType
s.set_age = MethodType(set_age, s)  # 绑定方法到实例
s.set_age(25)
print(s.age)  # 输出: 25

注意:这种方式绑定的方法只对当前实例有效。

1.3 动态绑定类方法

def set_score(self, score):
    self.score = score

Student.set_score = set_score  # 绑定方法到类

s1 = Student()
s2 = Student()
s1.set_score(100)
s2.set_score(90)
print(s1.score)  # 输出: 100
print(s2.score)  # 输出: 90

2. 使用 __slots__ 限制实例属性

虽然动态绑定很灵活,但有时我们需要限制实例可以拥有的属性。

2.1 基本用法

class Student:
    __slots__ = ('name', 'age')  # 只允许 name 和 age 属性

s = Student()
s.name = 'Michael'
s.age = 25
s.score = 99  # 抛出 AttributeError

2.2 继承中的 __slots__

  • 默认情况下,__slots__ 不会被子类继承
  • 如果子类也定义了 __slots__,则允许的属性是子类和父类 __slots__ 的并集
class GraduateStudent(Student):
    __slots__ = ('score',)  # 添加 score 属性

g = GraduateStudent()
g.name = 'Michael'
g.age = 25
g.score = 99  # 现在可以设置 score 属性

2.3 现代 Python 中的注意事项

  1. 性能考虑__slots__ 可以减少内存使用,因为实例不再需要 __dict__ 字典
  2. 与描述符的交互__slots__ 会为每个属性创建描述符
  3. @property 的兼容性__slots__ 不影响 property 的使用

3. 实际应用建议

  1. 当需要限制实例属性时使用 __slots__
  2. 对于大量实例的类,使用 __slots__ 可以节省内存
  3. 在框架开发中,__slots__ 可以防止用户添加意外属性
  4. 注意 __slots__ 会阻止 __dict__ 的创建,因此无法动态添加新属性

4. 示例代码

class RestrictedStudent:
    __slots__ = ('name', 'age', '_score')
    
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        if not isinstance(value, (int, float)):
            raise ValueError('Score must be numeric')
        self._score = value

rs = RestrictedStudent()
rs.name = 'Alice'
rs.age = 20
rs.score = 95  # 使用 property 设置 score
print(rs.score)  # 输出: 95

5. 总结

特性动态绑定__slots__
灵活性
内存使用较高较低
性能稍慢稍快
适用场景需要动态扩展需要严格控制属性

在现代 Python 开发中,合理使用 __slots__ 可以在保持代码清晰的同时提高性能,特别是在需要创建大量实例的情况下。