I want to preface this question by apologizing for its length, especially the code samples; however, I believe I have included the minimum necessary code to illustrate the differences in approaches.
I have a class with a number of instance attributes, and each of these attributes has some respective attributes.
For example, let's consider a GUI application where there are a number of attributes, and each attribute has a respective layout and field. It is often necessary to perform some action upon each layout or field as a group.
I have come up with 3 ways to organize the class which may satisfy the conditions above:
Naive Approach
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = username
self.username_layout = Layout()
self.username_field = Field()
self.fav_food = fav_food
self.fav_food_layout = Layout()
self.fav_food_field = Field()
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
if __name__ == '__main__':
if self.username_field.is_changed():
self.username = self.username_field.text
# Now I have to type out the mouthful `self.username_field`
# every time I want to access the field
# even though `self.username_field` is the only field in
# the scope of this method.
# I could do the same thing as the dict solution below:
# field = self.username_field
# But that also kind of goes against Python convention
def updateLayouts(self):
print("Doing something to Username Layout")
self.username_layout.doSomething()
print("Doing something to Favorite Food Layout")
self.fav_food_layout.doSomething()
# I now have to manually go through each layout and do the same thing, or I could do this:
for attr, value in self.__dict__.viewitems()
if attr.endswith("_layout") and isinstance(value, Layout):
print("Doing something to %s Layout" % attr) # Not equivalent to below answers
# Would need self.[attr]_display_name attributes
self.value.doSomething()
Advantages and Issues with the Naive Approach
This is the simplest approach to implement, and in simple cases may be fine. In this example, which only has username
and fav_food
it's not a big deal. However, this approach does not scale well.
This approach leads to some serious attribute bloat, and attributes with potentially very long names.
It's also a serious hassle to iterate through all the layouts or fields. The method of iterating through __dict__
used here is dependent upon the naming of the attributes- both that all desired layouts end with _layout
, and that no undesired attributes end with _layout
.
Dict Approach
This is the approach that I am currently using, but I began to suspect that I might be crazy and this might be a bad solution.
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = username
self.fav_food = fav_food
self.layouts = {}
self.fields = {}
self.layouts["Username"] = Layout()
self.layouts["Favorite Food"] = Layout()
self.fields["Username"] = Field()
self.fields["Favorite Food"] = Field()
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
field = self.fields["Username"]
if field.is_changed():
self.username = field.text
# Do a bunch of other stuff with field
def updateLayouts(self):
for key, value in self.layouts.viewitems():
print("Doing something to %s Layout" % key)
value.doSomething()
Advantages and Issues with the Dict Approach
I came to this approach out of a need to easily group together the related attributes, such as fields
and layouts
in this example.
In addition, this approach leads to nice, short names when accessing an attribute in limited scope, such as in captureUsername
.
This approach allows fairly straight-forward iteration that is sure to iterate over all of and only the desired attributes.
In terms of disadvantages, this approach is, to my knowledge, fairly unconventional. It could confuse people who read the code in the future.
The method of accessing an attribute in the dict, by providing a string, is a bit un-ideal. IDE's won't catch you if you make a mistake typing the key, nor will they provide auto-completion. You have to be very diligent about naming the keys consistently, or this approach could become a mess.
Object Oriented Approach
I actually came to this approach while I was thinking about this question and typing it into StackExchange. I'm not sure why I didn't think of it before. This is probably the most conventional approach; however, I do feel it has some disadvantages over the Dict Approach.
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = UserProfileItem(username, "Username", Layout(), Field())
self.fav_food = UserProfileItem(fav_food, "Favorite Food", Layout(), Field())
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
# This still feels a little verbose, but is broken up more nicely
if self.username.field.is_changed():
self.username.item = self.username.field.text
# Do a bunch of other stuff with self.username.field
def updateLayouts(self):
# You could go through the layout attributes manually,
# or you could use the class' __dict__ attribute, as below.
#
# This still seems less than ideal, as you have to iterate
# through all of the class' attributes, which could be many,
# and then check the type.
for attr, value in self.__dict__.viewitems():
if isinstance(value, UserProfileItem):
print("Doing something to %s Layout" % value.name)
value.layout.doSomething()
class UserProfileItem(object):
def __init__(self, item, name, layout, field):
self.item = item
self.name = name
self.layout = layout
self.field = field
Advantages and Disadvantages of the Object Oriented Approach
In an object oriented paradigm, this approach makes quite a bit of sense. An object allows you to group together related values with ease.
The Dict Approach, however, does a somewhat better job at accessing all Field
or Layout
attributes as a group, and iterating through them. It gives you exactly what you need, rather than iterating through all attributes of the instance, and requires less validation.
So, in conclusion, what is the preferred approach to this problem? Is my Dict approach inherently bad? Am I correct that the Object Oriented approach is preferable? Is there another approach to the problem that I haven't thought of?