Sorry, something went wrong.
There was a problem hiding this comment.
LGTM
(We could maybe think about whether it might be good to add __slots__ to some other typing classes, but that can be left for another PR -- _TypeVarLike is definitely the most clear-cut)
Sorry, something went wrong.
|
Whoa. Looking at the diff I realized for the first time that the two subclasses, TypeVar and ParamSpec, have a __dict__ slot, so adding slots to the base class does not actually make a difference. I'm sure that the __dict__ slots exist for a reason -- if we removed those, some user code would break that assigns additional attributes. Now I wonder if the problem isn't the inverse -- why do the two subclasses have slots? What does that add? |
Sorry, something went wrong.
|
@gvanrossum I can imagine the __slots__ still serve their purpose of improved attribute lookup speed 🤔 , although I of course don't know the reasoning when the code was written. I can imagine __dict__ was added in the end to enable class-like attributes (e.g. __module__, __doc__, etc.) to be set in user code. (Having __dict__ in __slots__ is explicitly allowed for this possibility.) Possible solutions:
|
Sorry, something went wrong.
|
Interesting. I admit that when I implemented ParamSpec, it was meant to mirror any existing behavior in TypeVar, and I didn't give much thought into whether it required a __dict__. Digging through commit history tells me that we only added a __dict__ to TypeVar recently (3.9-ish) c1c7d8e. Prior to this, TypeVar had __slots__ but no __dict__, all the way back to when typing was still in a separate package from Python. |
Sorry, something went wrong.
There was a problem hiding this comment.
Removing '__dict__' slot and running tests does not affect their result. All tests pass without it. It is also not documented.
I also cannot recall any real-life use-case where T.some_attr is used 🤔
I think that removing __dict__ from __slots__ might be a good idea in the long run.
Sorry, something went wrong.
|
@sobolevn I'm inclined to agree, although adding __doc__ to the allowed slots may be a good idea. |
Sorry, something went wrong.
|
I'd be very reluctant to remove the __dict__ slot -- even though it's undocumented and we don't test for it, someone is likely using it (since having a __dict__ is the default for most classes). I don't believe that attribute lookup speed is ever a factor for these. I suppose we could ask @serhiy-storchaka if he recalls why he added the __dict__ slot in #19719. It's possible that there's a space-saving reason for having __slots__ that include a __dict__ -- I'm guessing that as long as you don't use the __dict__ it's NULL, so for the common case (where all attributes fit in slots) the __slots__ version is more space-efficient. I used to just know this kind of stuff, but the code is a lot more convoluted now due to key-sharing (the dict keys are stored, once, on the class object, and the instances only have a values array), and is about to become more so -- for 3.11 Mark Shannon has improvements that store the values array in a more compact form (IIRC without holes). Anyway, I think the sensible solution is to remove the __slots__ from these classes. |
Sorry, something went wrong.
|
@ariebovenberg is right, __dict__ is needed for supporting the __module__ attribute in instances. You cannot add '__module__' in __slots__, because descriptor __module__ conflicts with the same-named class attribute. '__dict__' was explicitly added in __slots__ in c1c7d8e. Previously there was an implicit __dict__ inherited from _Immortal which lacked __slots__. After adding an empty __slots__ in _Immortal I was needed to add '__dict__' in __slots__ for TypeVar. Alternate solution would be to remove __slots__ in TypeVar, but I hoped to find other solution of this conflict sometime. There was no conflict for __name__, because __name__ of class is accessed via a metaclass descriptor. But __module__ of class is saved in class' dict. Adding a dedicated field in PyTypeObject and descriptor in class type would solve this conflict. It is a relatively large change which breaks binary compatibility of PyTypeObject (which is not in the stable API anyway) and may simplify and speed up access to __module__ attribute of classes. But there may be side effects. |
Sorry, something went wrong.
|
@serhiy-storchaka What do you think of just getting rid of the slots for TypeVar and ParamSpec? |
Sorry, something went wrong.
|
What do you think of just getting rid of the slots for TypeVar and ParamSpec? I am fine with this. |
Sorry, something went wrong.
|
@serhiy-storchaka @gvanrossum about the slots on _TypeVarLike: I assume we don't keep them there either, since we can assume it'll only be subclassed by ParamSpec and TypeVar? If its purpose would be a general mixin, __slots__ would make more sense. |
Sorry, something went wrong.
|
@serhiy-storchaka @gvanrossum about the slots on _TypeVarLike: I assume we don't keep them there either, since we can assume it'll only be subclassed by ParamSpec and TypeVar? Indeed. If its purpose would be a general mixin, __slots__ would make more sense. It is not. |
Sorry, something went wrong.
There was a problem hiding this comment.
LG, but please add a brief blurb.
Sorry, something went wrong.
| @@ -0,0 +1,2 @@ | |||
| Removed ``__slots__`` from :class:``typing.ParamSpec`` and :class:``typing.TypeVar``. | |||
There was a problem hiding this comment.
| Removed ``__slots__`` from :class:``typing.ParamSpec`` and :class:``typing.TypeVar``. | |
| Removed ``__slots__`` from :class:`typing.ParamSpec` and :class:`typing.TypeVar`. |
rst uses single backticks.
Sorry, something went wrong.
There was a problem hiding this comment.
whoops! Committed the fix below ✅
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks all! Landing now.
Should we bother backporting? It doesn't make a difference, so probably not.
Sorry, something went wrong.
|
@Fidget-Spinner Do you want to land this? Make sure to edit the commit title and message so they match what's ultimately done. |
Sorry, something went wrong.
|
@Fidget-Spinner: Please replace # with GH- in the commit message next time. Thanks! |
Sorry, something went wrong.
|
@gvanrossum Sorry, I didn't realize that GH mobile app's squash and merge doesn't allow me to edit the message. After pressing the button it just merged it without prompting me for anything 😞. Lesson learnt: always do the major things on the computer. I'll revert and redo the message later today. Anyways, @ariebovenberg thanks for the patch, and congrats on your first contribution to CPython! |
Sorry, something went wrong.
|
@gvanrossum Sorry, I didn't realize that GH mobile app's squash and merge doesn't allow me to edit the message. After pressing the button it just merged it without prompting me for anything 😞. Their mobile website allows it though. :-) Lesson learnt: always do the major things on the computer. I'll revert and redo the message later today. I wouldn't bother. The summary line is correct: "Remove slots from typing.TypeVar, .ParamSpec ". The commit body is spammy, but that doesn't matter enough to revert and fix the commit. We can live with this! Anyways, @ariebovenberg thanks for the patch, and congrats on your first contribution to CPython! Indeed. |
Sorry, something went wrong.
The mixin class "typing._TypeVarLike" has no __slots__. Its subclasses do define __slots__, so it looks like a mistake.
This was confirmed in the bug report
https://bugs.python.org/issue46244