Reputation: 63
When I update an instance property, that change is not reflected in the __str__
method of the parent class. This makes sense, as super().__init__
is called during the __init__
call, and when I make an update to the property, it's not getting called again.
class BulkData:
def __init__(self, fields):
self.fields = fields
def __str__(self):
return ''.join(str(f) for f in self.fields)
class Node(BulkData):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
fields = [self.x, self.y, self.z]
super().__init__(fields)
Example:
n1 = Node(1,2,3)
n1.x = 5
print(n1)
> '123'
I would like the result to be '523'
.
Upvotes: 0
Views: 404
Reputation: 530970
You can define actual Python properties that reflect the state of fields
:
class BulkData:
def __init__(self, fields, floats=None):
if floats is None:
floats = []
self.fields = fields
self.floats = floats
def __str__(self):
return ''.join([str(x) for x in self.fields])
class Node(BulkData):
def __init__(self, x, y, z):
super().__init__([x, y, z])
@property
def x(self):
return self.fields[0]
@x.setter(self, v):
self.fields[0] = v
@property
def y(self):
return self.fields[1]
@y.setter(self, v):
self.fields[1] = v
@property
def z(self):
return self.fields[2]
@z.setter(self, v):
self.fields[2] = v
Instead of instance attributes, you define property
instances that "wrap" a particular slot of the underlying fields
attribute.
If you are thinking, that's a lot of code... you're right. Let's fix that.
class FieldReference(property):
def __init__(self, pos):
def getter(self):
return self.fields[pos]
def setter(self, v):
self.fields[pos] = v
super().__init__(getter, setter)
class Node(BulkData):
def __init__(self, x, y, z):
super().__init__([x, y, z])
x = FieldReference(0)
y = FieldReference(1)
z = FieldReference(2)
Here, we subclass property
and hard-code the getter and setter functions to operate on the position specified by the argument to the property. Note that self
in getter
and setter
are distinct from self
in __init__
, but getter
and setter
are both closures with access to __init__
's argument pos
.
You might also want to read the Descriptor how-to, specifically the section on properties to understand why FieldReference
is defined the way it is.
Upvotes: 1
Reputation: 19404
You mention in your question "property" but those are just attributes. One way to achieve that is to actually use a property
:
class BulkData:
def __init__(self, fields):
self.fields = fields
def __str__(self):
return ''.join(self.fields)
class Node(BulkData):
def __init__(self, x, y, z):
self._x = x
self._y = y
self._z = z
fields = [self._x, self._y, self._z]
super().__init__(fields)
@property
def x(self):
return self._x
@x.setter
def x(self, val):
self._x = val
self.fields[0] = val
And now:
>>> n1 = Node('1','2','3')
>>> n1.x = '5'
>>> print(n1)
523
Upvotes: 2