Python dynamic property binding and__slots__Tutorial

When developing entity classes with a slightly larger amount of data (such as e-commerce product models, user information classes on social platforms), have you also stepped through these pitfalls?

  • A colleague randomly added a bunch of instances to a student during testing.s.temp_xxxI forgot to delete the temporary attributes. As a result, the data was confused during subsequent debugging, and the problem could not be found for a long time.
  • After the system went online, it was discovered that hundreds of thousands of small objects occupied far more memory than expected, and the server resources directly alarmed.
  • When taking over a piece of legacy code, it is impossible to figure out which core attributes are "specified" by a certain class. You can only go through all the instantiations, which is very costly to maintain.

At this time, Python’s__slots__It can help you control these "excessive freedom" behaviors. But before we get into it, we need to briefly review one of Python's most flexible features: Dynamic binding of properties and methods at runtime.


1. Python’s dynamic binding mechanism

Python is a purely dynamic language. Unlike Java and C++, class attributes and methods are locked during compilation. While the code is running, you can add new properties or functions to a single instance or even the entire class at any time, which is extremely flexible.

1.1 Dynamically bind attributes to a single instance

This is the most basic operation: just assign a property name to the instance that does not exist.

class Student:
    """一个空的类,什么都没定义"""
    pass

# 创建两个学生实例
s1 = Student()
s2 = Student()

# 只给 s1 单独绑定一个 name 属性
s1.name = "张三"
print(s1.name)   # 输出:张三

# s2 并没有这个属性
# print(s2.name) # 报错!AttributeError: 'Student' object has no attribute 'name'

Although this operation is arbitrary, it can easily lead to different instances of the same class having completely different sets of properties, and the code will become difficult to maintain.

1.2 Dynamically bind methods to a single instance

Not only attributes, but also methods can be bound to only a certain instance. But please note: you must passtypes.MethodTypeWrap the function and use the instance itself asselfPass it in.

from types import MethodType

def set_gender(self, gender):
    """要绑到实例上的新方法"""
    self.gender = gender

# 只给 s1 绑定 set_gender 方法
s1.set_gender = MethodType(set_gender, s1)
s1.set_gender("男")
print(s1.gender)   # 输出:男

# s2 没有这个方法
# s2.set_gender("女")  # 报错!AttributeError

1.3 Dynamically bind properties/methods to the entire class

If you want all instances to use a new property or method, you can assign a value directly to the class. In this way, both the old instance created before and the new instance created later will have this new member.

def set_grade(self, grade):
    """要绑到 Student 类上的方法"""
    self.grade = grade

# 直接给类绑定
Student.set_grade = set_grade

# 所有实例都能用了
s2.set_grade(3)
print(s2.grade)   # 输出:3

s1.set_grade(4)
print(s1.grade)   # 输出:4

Dynamic binding gives Python great flexibility, which is especially suitable for writing prototypes and doing temporary testing. But "excessive freedom" also leads to the problems mentioned earlier. Is there a way to retain the convenience of animal binding while also putting a little "tightening spell" on the animal?__slots__That's the answer.


2. Use__slots__Put a "tightening spell" on the class

__slots__Is a class variable, usually defined as a tuple or list, which lists the attribute names that instances of the class can only have. Once defined, instances cannot add attributes that are not in this whitelist.

2.1 The most basic usage

Declared directly inside the class__slots__ = ('允许的属性1', '允许的属性2', ...)That’s it.

class Student:
    __slots__ = ("name", "age")   # 只允许绑定 name 和 age

s = Student()
s.name = "李四"
s.age = 22

# 试图绑定 score 就会立即报错
# s.score = 99  # ❌ AttributeError: 'Student' object has no attribute 'score'

In this way, each instance can only support attributes in the whitelist, avoiding data pollution caused by "sliding" and making the intention of the code clearer: see__slots__Just know what core attributes this class has.

2.2 Things to note in inheritance scenarios

When a class has an inheritance relationship,__slots__There are two pitfalls that are easy to step into:

  1. Subclasses do not inherit from parent classes by default.__slots__limit If the parent class defines__slots__, but the subclass is not explicitly defined, then instances of the subclass will also have the attributes allowed by the parent class + their own default__dict__(That is, you can still add any attributes dynamically), then__slots__The memory optimization effect will also disappear.

  2. Explicit definition of subclasses__slots__Finally, the allowed attributes are the union of "parent class + child class" subclass__slots__You only need to declare the additional attributes you need, and there is no need to repeat the attributes already listed in the parent class.

class GraduateStudent(Student):
    __slots__ = ("score",)   # 只添加自己独有的 score

g = GraduateStudent()
g.name = "王五"     # ✅ 继承自父类的白名单
g.age = 25          # ✅ 继承自父类的白名单
g.score = 98        # ✅ 子类自己添加的

# g.temp_grade = 1  # ❌ 仍然报错,因为没有进白名单

Tips: If you don’t need additional attributes in the subclass and want to strictly inherit everything from the parent class__slots__Restrictions can be written in subclasses__slots__ = ()(empty tuple), which maintains the limit but does not enable__dict__

2.3 Modern Python development in progress__slots__The three hidden values ​​of

🔹 Save memory (the most important thing)

Python assigns one to each instance by default__dict__Dictionary to store attributes. The dictionary itself is a very heavy structure. When the number of instances reaches hundreds of thousands or even millions, the memory overhead is considerable. Instead of using__slots__After that, Python no longer creates instances for__dict__, instead using a more compact fixed space to store attribute values, the memory footprint can be reduced several times or even more.

🔹 with@propertyPerfectly compatible

Some students are worried: using__slots__can no longer be used@propertyThe decorator does the attribute checksum calculation. In fact, it’s nothing to worry about. Just put@propertyThe corresponding private variables are also added__slots__Just whitelist.

class RestrictedStudent:
    __slots__ = ("name", "age", "_score")   # 别忘了放私有变量 _score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, (int, float)) or not (0 <= value <= 100):
            raise ValueError("分数必须是 0~100 的数字")
        self._score = value

rs = RestrictedStudent()
rs.name = "赵六"
rs.age = 21
rs.score = 95         # ✅ 通过 setter 设置
print(rs.score)       # 输出:95

# rs.score = "A"      # ❌ 抛出 ValueError,被 property 拦截

🔹 When shouldn't be used__slots__

  • If your class needs frequently dynamically extended attributes (for example, when writing a crawler, insert a new attribute into the instance every time a field is grabbed), don't use it__slots__, otherwise the flexibility will be lost.
  • If you have a lot of dependencies in your projectobj.__dict__orvars(obj)To do batch operations on attributes (such as serialization, reflection), use__slots__This will make all these codes invalid.

3. Suggestions for practical application scenarios

according to__slots__characteristics, the following situations are its best uses:

  • Entity classes created in bulk For example, a single record in log analysis, candidate products in recommendation systems, and transaction snapshots in high-frequency trading. The memory saving effect is very obvious.

  • Internal classes of framework/library It can prevent users from adding attributes to your core objects by "slipping" to a certain extent, ensuring the stability of the framework during runtime.

  • Business classes that require strict control of attributes For example, the account class of the financial system is only allowed to defineidbalancecreate_timeand other fixed fields to prevent data from being accidentally contaminated or tampered with.


4. Quick comparison summary

FeaturesDynamic binding (default)Use__slots__
FlexibilityVery high, you can add any properties/methods at any timeLow, you can only use predefined whitelist properties
Memory usageHigher (shipped with each instance__dict__Dictionary)Lower (uses compact fixed storage structure)
Attribute access speedSlightly slower (dictionary lookup)Slightly faster (direct access based on offset)
Whether to support__dict__✅ Support❌ Not available by default (unless manually set__dict__Add to whitelist)
Applicable scenariosClasses that require flexible expansion and rapid prototypingLarge-scale instances, stable business classes that require strict control of attributes

__slots__It has never been a "must have option" for Python development, but using it well in the appropriate scenarios can make your code more stable, save more memory, and have better performance. Next time you design a class that will be instantiated a lot, you might as well ask yourself: "Are the properties of this class already fixed? Do you need to use__slots__Come and take care of it? "