How to refactor a Python “god class”?
Posted
by
Zearin
on Programmers
See other posts from Programmers
or by Zearin
Published on 2012-03-28T16:45:44Z
Indexed on
2012/03/28
17:43 UTC
Read the original article
Hit count: 410
Problem
I’m working on a Python project whose main class is a bit “God Object”. There are so friggin’ many attributes and methods!
I want to refactor the class.
So Far…
For the first step, I want to do something relatively simple; but when I tried the most straightforward approach, it broke some tests and existing examples.
Basically, the class has a loooong list of attributes—but I can clearly look over them and think, “These 5 attributes are related…These 8 are also related…and then there’s the rest.”
getattr
I basically just wanted to group the related attributes into a dict-like helper class. I had a feeling __getattr__
would be ideal for the job. So I moved the attributes to a separate class, and, sure enough, __getattr__
worked its magic perfectly well…
At first.
But then I tried running one of the examples. The example subclass tries to set one of these attributes directly (at the class level). But since the attribute was no longer “physically located” in the parent class, I got an error saying that the attribute did not exist.
@property
I then read up about the @property
decorator. But then I also read that it creates problems for subclasses that want to do self.x = blah
when x
is a property of the parent class.
Desired
- Have all client code continue to work using
self.whatever
, even if the parent’swhatever
property is not “physically located” in the class (or instance) itself. - Group related attributes into dict-like containers.
- Reduce the extreme noisiness of the code in the main class.
For example, I don’t simply want to change this:
larry = 2
curly = 'abcd'
moe = self.doh()
Into this:
larry = something_else('larry')
curly = something_else('curly')
moe = yet_another_thing.moe()
…because that’s still noisy. Although that successfully makes a simply attribute into something that can manage the data, the original had 3 variables and the tweaked version still has 3 variables.
However, I would be fine with something like this:
stooges = Stooges()
And if a lookup for self.larry
fails, something would check stooges
and see if larry
is there. (But it must also work if a subclass tries to do larry = 'blah'
at the class level.)
Summary
- Want to replace related groups of attributes in a parent class with a single attribute that stores all the data elsewhere
- Want to work with existing client code that uses (e.g.)
larry = 'blah'
at the class level - Want to continue to allow subclasses to extend, override, and modify these refactored attributes without knowing anything has changed
Is this possible? Or am I barking up the wrong tree?
© Programmers or respective owner