I'm trying to use python properties and inheritance and something isn't behaving intuitively. I want to be able to utilize property getters and setters of the inherited class to avoid having to repeat code for repeated behavior.
In order to boil down my problem I created the following example. I have a Car with behavior for counting passengers and populating the car's seats when given some number of passengers (using the occupants property getter and setter). For a van with 3 rows of seats I should only have to define the behavior of the 3rd seat and defer to the inherited Car class for the first 2 rows...
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
@property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
@occupants.setter
def occupants(self, val):
#Start with an empty car
self._contents['row1'] = 0
self._contents['row2'] = 0
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
@property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
@occupants.setter
def occupants(self, val):
#Start with an empty van (Car class handles row1 and row2)
self._contents['row3'] = 0
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
#This causes an AttributeError
super(Van, self).occupants = 4
else:
#This causes an AttributeError
super(Van, self).occupants = val
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
The output I get is as follows:
Van has 3 people
Seating 6 people in the van...
Traceback (most recent call last):
File "C:/scratch.py", line 74, in <module>
van.occupants = 6
File "C:/scratch.py", line 65, in occupants
super(Van, self).occupants = 4
AttributeError: 'super' object has no attribute 'occupants'
Process finished with exit code 1
What's especially interesting to me is that the superclass's getter is working fine, but when I try to use the setter I get the attribute error. Am I using super()
incorrectly? Is there a better way to do this?
My actual application involves reading/writing between a text file format and a dictionary-like data structure. Some of the stuff in the text file is parsed out by my base class and some other special parameters are handled by the subclass. In the subclass setter I want to start by letting the base class parse whatever it needs from the text file (populating the data structure), then let the subclass parse out additional values for storage in the inherited data structure.
Some research lead me to this and eventually Issue 505028 where it is declared a "feature" instead of a bug. So with that out of the way, is there a way to make the above logic work using properties and inheritance? Do I have to use Car.occupants.fset(self, 4)
or something? I may answer myself in a minute, but I'm going to go ahead and post this to share it with folks. SOrry if it's a duplicate.
edit: Fixed some additional bugs like emptying all seats before setting occupants and the numeric logic in the Van occupants setter was wrong and incomplete (only became evident once the property error was fixed).