Reputation: 1299
I am attempting to use a property-based method to control the class constructor (screening for bad values at instance creation time), in this code. For some reason I don't understand the function set_sequence
won't alter the derived class Binary object's attribute seq
when called from the @num.setter
method - it has to be called elsewhere. Am I missing something basic about how properties work?
class Number:
def __init__(self, num):
self.num = num
class Binary(Number):
def __init__(self, num):
super().__init__(num)
self.seq = ()
@property
def num(self):
return self._num
@num.setter
def num(self, value):
if value < 0:
raise ValueError('Unsigned numbers cannot be negative')
self._num = value
self.set_sequence() #this calls the function, but the value doesn't get bound
def set_sequence(self):
print('called ', end='')
self.seq = tuple([int(x) for x in bin(self.num)[2:]])
Calling this code as follows:
if __name__ == "__main__":
n1 = Binary(11)
print(f"{n1.num} = {n1.seq}")
n1.set_sequence()
print(f"{n1.num} = {n1.seq}")
Gives:
called 11 = ()
called 11 = (1, 0, 1, 1)
This throws an exception as expected when negative values are passed to constructor, but I don't understand why the function call fails to behave as expected. This pattern is based on Brett Slatkin's Item#29 in 'Effective Python' incidentally, the part about using @property to do type checking and value validation when the constructor is called.
Upvotes: 0
Views: 60
Reputation: 881
To have the desired output, you should do like this. In you example self.set_sequence()
is overridden by the second instruction in the constructor.
class Number:
def __init__(self, num):
self.num = num
class Binary(Number):
seq = ()
def __init__(self, num):
# or eventually here
# seq = ()
super().__init__(num)
@property
def num(self):
return self._num
@num.setter
def num(self, value):
if value < 0:
raise ValueError('Unsigned numbers cannot be negative')
self._num = value
self.set_sequence() #this calls the function, but the value doesn't get bound
def set_sequence(self):
print('called ', end='')
self.seq = tuple([int(x) for x in bin(self.num)[2:]])
if __name__ == "__main__":
n1 = Binary(11)
print(f"{n1.num} = {n1.seq}")
n1.set_sequence()
print(f"{n1.num} = {n1.seq}")
Upvotes: 1
Reputation: 4510
Because in your constructor after super().__init__(num)
that calls your @num.setter
you use self.seq = ()
that overrides the value stored in your setter method.
Upvotes: 2