I’m one of the maintainers of attrs so that’s the perspective I’m posting this from, but these feature requests are wholly applicable to dataclasses as well.
This is my wishlist for making slot classes better in the two libraries, since I’m a big fan of slot classes. I’m reaching out to core devs to see if any of these are possible.
- Add slotness to a class after the fact.
Right now, when creating a slot class, we need to recreate the class from scratch to add slots to it. We’ve tried to make this as smooth as possible, but new edge cases keep popping up, like needing to rewrite closure cells in deeply decorated methods, and
class.__subclasses__() still containing the old class until a GC collection.
It would be awesome if there was a way to add slotness to a class instead of needing to clone it. It could be a low-level thing, tucked away somewhere uninteresting to civilians. We only need it one-way, dict → slots.
- Speed up frozen slot
__init__by creating slot descriptors for read-only fields
Right now, frozen slot classes incur a slight speed penalty on creation. (IIRC dict classes avoid this by using the instance
This is because frozen classes have a special
__setattr__ to simulate immutability. So inside
__init__, slot classes use a cached version of
object.__setattr__ to set their fields for the first time. This is slightly slower than the non-frozen path.
Frozen classes are a great tool for eliminating some classes of bugs and it bothers me I need to pick between performance and (a slightly better chance of) correctness, even though the performance penalty is minor.
To give context, here’s my understanding of how slot classes work (corrections welcome). Each field value is stored somewhere hidden, and when a slot class is created the interpreter creates a descriptor for each field and puts it on the class, under the field name. This magical descriptor handles writes and reads from each field.
My proposal: the interpreter should expose a function that can copy this descriptor into a read-only clone. Attrs and dataclasses could then stash away the original descriptors into a well-known location (they are still needed inside of
__init__), and put the clone, read-only descriptors on the class.