Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-90562: Update __class__ in function closures for dataclasses with slots=True #104038

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

wookie184
Copy link
Contributor

@wookie184 wookie184 commented May 1, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting review interpreter-core (Objects, Python, Grammar, and Parser dirs)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants