You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generate wrapper function for `__setattr__` and set it as the
`tp_setattro` slot. The wrapper doesn't have to do anything because
interpreted python runs the defined `__setattr__` on every attribute
assignment. So the wrapper only reports errors on unsupported uses of
`__setattr__` and makes the function signature match with the slot.
Since `__setattr__` should run on every attribute assignment, native
classes with `__setattr__` generate calls to this function on attribute
assignment instead of direct assignment to the underlying C struct.
Native classes generally don't have `__dict__` which makes implementing
dynamic attributes more challenging. The native class has to manage its
own dictionary of names to values and handle assignment to regular
attributes specially.
With `__dict__`, assigning values to regular and dynamic attributes can
be done by simply assigning the value in `__dict__`, ie.
`self.__dict__[name] = value` or `object.__setattr__(self, name,
value)`. With a custom attribute dictionary, assigning with `name` being
a regular attribute doesn't work because it would only update the value
in the custom dictionary, not the actual attribute.
On the other hand, the `object.__setattr__` call doesn't work for
dynamic attributes and raises an `AttributeError` without `__dict__`.
So something like this has to be implemented as a work-around:
```
def __setattr__(self, name: str, val: object) -> None:
if name == "regular_attribute":
object.__setattr__(self, "regular_attribute", val)
else:
self._attribute_dict[name] = val
```
To make this efficient in native classes, calls to `object.__setattr__`
or equivalent `super().__setattr__` are transformed to direct C struct
assignments when the name literal matches an attribute name.
0 commit comments