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
dataclass(slots=True) does not account for slots in base classes #90540
Comments
@DataClass(slots=True) adds slots to dataclasses. It adds a slot per field. >>> class Base:
... __slots__ = ('a', )
...
>>> @dataclass(slots=True)
... class Foo(Base):
... a: int
... b: float
...
>>> Foo.__slots__
('a', 'b') # should be: ('b', ) The __slots__ documentation says:
Solution: don't add slots which are already defined in any base classes: >>> @dataclass
... class Bla(Base):
... __slots__ = ('b', )
... a: int
... b: float
...
>>> Bla(4, 5.65)
Bla(a=4, b=5.65) If you agree, I'd like to submit a PR to fix this. I already have a prototype working. |
I'll have to do some more research. But your analysis looks correct to me, so far. |
There are already 2 complexities I can think of:
|
Arie, can you please explain what is the technical difference between these two cases: class A:
__slots__ = ('a', )
# fields
class B(A):
__slots__ = ('a', 'b')
# fields And: class C:
__slots__ = ('a', )
# fields
class D(C):
__slots__ = ('b', )
# fields ? |
Both will function, but class B will add its slots after A's, causing there to be an extra unused slot in the object that you can only access by directly using the A.a descriptor. So all slotted inheriting dataclasses cause the object to use more memory than necessary. |
Spencer is correct. The documentation even adds: "This renders the meaning of the program undefined." It's clear it doesn't break anything users would often encounter (we would have heard about it), but it's still undefined behavior. |
It would also be interesting to see what attrs does in this case. |
>>> @attrs.define
... class C(Base):
... a: int
... b: int
...
>>> C.__slots__
('b', '__weakref__') We've got a test specifically for this use case: https://github.com/python-attrs/attrs/blob/5f36ba9b89d4d196f80147d4f2961fb2f97ae2e5/tests/test_slots.py#L309-L334 |
@hynek interesting! The discussion in python-attrs/attrs#420 on the weakref slot is very interesting as well. Considering __weakref__ is something we don't want to make impossible in dataclasses, @eric.smith what would be your preferred solution? |
@eric.smith did you give this some thought? Would we want to imitate the attrs behavior regarding __weafref__? It'd be nice if I can submit a PR to be included in 3.11 |
__slotnames__ is used for storing all slot names. New object.__getstate__() proposed in bpo-26579 may have some relation to this discussion. |
Serhiy: Could you point to some documentation on __slotnames__? I see a few references in the code to it, but it's not set on simple test class. >>> class A:
... __slots__=('a',)
...
>>> A.__slotnames__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'A' has no attribute '__slotnames__'. Did you mean: '__slots__'?
>>> |
|
Sorry, the previous message was written via a phone because of blackout, so the formatting is a tine bit messy. __slotnames__ is set in copyreg._slotnames(). If you want to keep a list of all slots you should save it in __slotnames__, either using copyreg._slotnames() directly or by duplicating its code. But as for the original issue, I do not think that the current behavior is incorrect. At least it is consistent with the behavior of regular classes. |
I don't have a problem saying that for a class to be used as a base class for a dataclass, its __slots__ must not be an iterator that's been exhausted. That doesn't seem like a very onerous requirement. I'm also not concerned about people using __slots__ to iterate over the fields, but I agree that a documentation note couldn't hurt. @ariebovenberg: go ahead and submit your PR. Thanks! |
@eric.smith awesome! What would you like to do about the __weakref__ slot?
I wouldn't recommend (1), because sooner or later this feature request will pop up. It seems reasonable to support it. |
I thought there was an existing issue that covered this, but now I can't find it. I'd prefer #2, create a separate issue. |
Thanks for all of your work, @ariebovenberg! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: