class D:
def __init__(self):
self._attr = 1
self._attr2 = 2
def __getattr__(self, name):
if name == 'data':
return (self._attr, self._attr2)
def __setattr__(self, name, value):
print('__setattr__')
if name == 'data':
self._attr, self._attr2 = value
else:
super().__setattr__(name, value)
d = D()
print(d.data)
#OUTPUT:
#__setattr__
#__setattr__
#(1,2)
For some reason the line return (self._attr, self._attr2)
behaves as an assignment to self._attr
and self._attr2
, but why?
I consulted Google and Gemini, the Mark Lutz book, but couldn't figure it out.
It's not coming from the print(d.data)
line. Comment it out. It is from setting the attributes in __init__
. Extend the print in __setattr__
to print(f'__setattr__({name=}, {value=})')
to see more information:
class D:
def __init__(self):
self._attr = 1
self._attr2 = 2
def __getattr__(self, name):
if name == 'data':
return (self._attr, self._attr2)
def __setattr__(self, name, value):
print(f'__setattr__({name=}, {value=})')
if name == 'data':
self._attr, self._attr2 = value
else:
super().__setattr__(name, value)
d = D()
#print(d.data)
Output:
__setattr__(name='_attr', value=1)
__setattr__(name='_attr2', value=2)
Note you can also get the same behavior implementing data
as a property and have more clear code:
class D:
def __init__(self):
self._attr = 1
self._attr2 = 2
@property
def data(self):
return (self._attr, self._attr2)
@data.setter
def data(self, value):
self._attr, self._attr2 = value
d = D()
print(d.data)
d.data = 4,5
print(d.data)
Output:
(1, 2)
(4, 5)