如何优化__slots__类的初始化速度?兼顾创建与属性访问效率
Great question—this digs into some fascinating details about how Python prioritizes optimization for built-in types versus user-defined classes. Let’s break this down clearly.
Why is dict initialization so much faster?
The core gap comes down to C-level optimization vs. Python-level execution overhead:
- Dictionaries are a foundational built-in type, implemented entirely in C. Their creation, key insertion, and access paths are hyper-optimized with minimal overhead—no Python bytecode to run, no layers of function calls, just direct memory operations and specialized fast-path checks.
- Your
__slots__class, while faster for attribute access, relies on a Python-defined__init__method. Even with__slots__eliminating per-instance dictionaries, the__init__still runs through Python’s interpreter: handling argument passing, executing attribute assignment bytecode, and navigating Python’s attribute lookup logic. This adds measurable overhead compared to the C-optimized dict operations. - Python’s interpreter also has special optimizations for common dict syntax (like literal creation
{'B':10}) that skip general-purpose checks, making these operations even quicker.
How to speed up slots class initialization?
Here are practical, actionable ways to cut down initialization overhead:
1. Use @dataclass(slots=True) for optimized generated code
The dataclasses module generates highly optimized __init__ methods under the hood (leveraging C-backed code generation where possible), which are far faster than hand-written Python __init__ methods. Here’s how to adapt your class:
from dataclasses import dataclass import timeit @dataclass(slots=True) class A: a: int # Test the optimized class %timeit A(10).a %timeit {'B': 10}['B']
You’ll see a significant speedup compared to your original hand-written __init__—the generated init avoids most Python-level overheads of a custom method.
2. Use __new__ instead of __init__ for direct attribute setup
Since __new__ is responsible for creating the instance (and runs at a lower level than __init__), you can set attributes directly in __new__ to skip some __init__ overhead. If you define __new__ without a custom __init__, Python uses the default C-implemented __init__ (which does nothing), saving you from executing Python bytecode for initialization:
import timeit class A: __slots__ = ('a',) def __new__(cls, a): instance = super().__new__(cls) instance.a = a return instance %timeit A(10).a %timeit {'B': 10}['B']
This gives a small but noticeable boost over a hand-written __init__.
3. Keep __init__ as lean as possible
If you stick with a custom __init__, eliminate all unnecessary work—no extra computations, conditionals, or function calls. The more you limit the method to just attribute assignments, the faster it will run.
4. For extreme cases: Use C extensions or Cython
If you need near-dict initialization speeds (and don’t mind stepping outside pure Python), writing a C extension or using Cython to define your __slots__ class can eliminate almost all Python-level overhead. This is overkill for most use cases, but critical for performance-sensitive code.
Final Notes
Even with these optimizations, dicts will likely stay slightly faster for simple key-value storage due to their deep integration with Python’s core. But for use cases where you need the structure, type safety, or method support of a class (not just a loose dict), these tweaks make __slots__ classes much more competitive in initialization speed.
内容的提问来源于stack exchange,提问作者Steven G

