Open df79943f-4aee-4531-a00d-c6b12816eb70 opened 5 years ago
>> class a(dict): __slots = '__dict', 'x'
>> class b(dict): __slots = '__dict', 'x'
>>> self = a(); self.__class__ = b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
self=a(); self.__class__ = b
TypeError: __class__ assignment: 'b' object layout differs from 'a'
This always occurs when __dict and/or __weakref are defined as slots, even when both classes have otherwise identical slots. This behavior appears to contradict what the docs say wrt to __class__ assignment, which is (in its entirety):
"__class assignment works only if both classes have the same __slots. "
Not sure if this is just a case of ambiguous documentation and intentional behavior or not. Since two classes with identical slots will always have identical internal struct layouts, I can't see a reason for this error.
The relevant logic is in the compatible_for_assignment() function on line 3972 in Objects/typeobject.c.
After checking that the tp_free slots are the same, it proceeds as follows:
/*
It's tricky to tell if two arbitrary types are sufficiently compatible as
to be interchangeable; e.g., even if they have the same tp_basicsize, they
might have totally different struct fields. It's much easier to tell if a
type and its supertype are compatible; e.g., if they have the same
tp_basicsize, then that means they have identical fields. So to check
whether two arbitrary types are compatible, we first find the highest
supertype that each is compatible with, and then if those supertypes are
compatible then the original types must also be compatible.
*/
So what is happening is that "class a" and "class b" aren't being directly compared to one another. Instead, they are being compared to their parent "dict". Since *dict* doesn't have __dict or __weakref, "a" and "b" are deemed to have incompatible layouts. See lines 3951 to 3954 in same_slots_added() in Objects/typeobject.c.
Possibly related ipython bug: autoreload gratuitous "object layout differs" error
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: ```python assignee = None closed_at = None created_at =
labels = ['3.7']
title = "Can't reassign __class__ despite the assigned class having identical slots"
updated_at =
user = 'https://github.com/mr-nfamous'
```
bugs.python.org fields:
```python
activity =
actor = 'rhettinger'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = []
creation =
creator = 'bup'
dependencies = []
files = []
hgrepos = []
issue_num = 35048
keywords = []
message_count = 2.0
messages = ['328296', '357983']
nosy_count = 9.0
nosy_names = ['rhettinger', 'mark.dickinson', 'pitrou', 'benjamin.peterson', 'serhiy.storchaka', 'thatiparthy', 'bup', 'xtreak', 'Sterling Smith']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue35048'
versions = ['Python 3.7']
```