gh-90562: Update __class__ in function closures for dataclasses with slots=True by wookie184 · Pull Request #104038 · python/cpython
Opened as a draft as I haven't updated docs or added news yet.
This is similar to how attrs does it, which was mentioned in the issue. https://github.com/python-attrs/attrs/blob/7c44cfb3c196f547d351af44e411b43f65819714/src/attr/_make.py#L876-L897.
As well as attrs functionality, this also supports:
- Functions decorated with decorators made with
functools.wraps - Properties without getters
There are some cases that are not supported:
- Functions decorated with decorators that don't use
functools.wraps(or more precisely, don't have a__wrapped__attribute which allows us to access the original function defined in the class). - Any other reason that the function is no longer in the class dictionary, any examples of that I can think of are pretty obscure though.
Note that since the __class__ cell is shared between functions, these unsupported methods will work if there is one occurence of a supported way of doing things. This is a bit of a gotcha, but I think if it's documented it should be acceptable.
I made an attempt at getting an idea of what the effect of this on performance will be on existing code
def make_dataclass(): @dataclass(slots=True) class Foo: a: int b: int c: int d: int e: int f: int g: int def h(self): ... def i(self): ... def j(self): ... def k(self): ... def l(self): ... def m(self): ... def n(self): ... def o(self): ... def p(self): ... def q(self): ... print(timeit.repeat(make_dataclass, number=1000, repeat=3))
Before: [0.9140953719997924, 0.9357239580003807, 0.9163554130000193]
After: [0.9437777250004729, 0.9911788059998798, 0.9630459829995743]
There is a difference, but I think it should be acceptable given it only affects dataclass creation.