If you want a class with "locked-down" instance attributes, it's not hard to make one, e.g.:
class LockedDown(object):
__locked = False
def __setattr__(self, name, value):
if self.__locked:
if name[:2] != '__' and name not in self.__dict__:
raise ValueError("Can't set attribute %r" % name)
object.__setattr__(self, name, value)
def _dolock(self):
self.__locked = True
class Example(LockedDown):
def __init__(self):
self.mistakes = 0
self._dolock()
def onemore(self):
self.mistakes += 1
print self.mistakes
def reset(self):
self.mitsakes = 0
x = Example()
for i in range(3): x.onemore()
x.reset()
As you'll see, the calls to x.onemore
work just fine, but reset
raises an exception because of the mis-spelling of the attribute as mitsakes
. The rules of engagement here are that __init__
must set all attributes to initial values, then call self._dolock()
to forbid any further addition of attributes. I'm exempting "super-private" attributes (ones starting with __
), which stylistically should be used very rarely, for totally specific roles, and with extremely limited scope (making it trivial to spot typos in the super-careful inspection that's needed anyway to confirm the need for super-privacy), but that's a stylistic choice, easy to reverse; similarly for the choice to make the locked-down state "irreversible" (by "normal" means -- i.e. requiring very explicit workaround to bypass).
This doesn't apply to other kinds of names, such as function-local ones; again, no big deal because each function should be very small, and is a totally self-contained scope, trivially easy to inspect (if you write 100-lines functions, you have other, serious problems;-).
Is this worth the bother? No, because semi-decent unit tests should obviously catch all such typos with the greatest of ease, as a natural side effect of thoroughly exercising the class's functionality. In other words, it's not as if you need to have more unit tests just to catch the typos: the unit tests you need anyway to catch trivial semantic errors (off-by-one, +1 where -1 is meant, etc., etc.) will already catch all typos, too.
Robert Martin and Bruce Eckel both articulated this point 7 years ago in separate and independent articles -- Eckel's blog is temporarily down right now, but Martin's right here, and when Eckel's site revives the article should be here. The thesis is controversial (Jeff Attwood and his commenters debate it here, for example), but it's interesting to note that Martin and Eckel are both well-known experts of static languages such as C++ and Java (albeit with love affairs, respectively, with Ruby and Python), and they're far from the only ones to have discovered the importance of unit-tests... and how a good unit-tests suite, as a side effect, makes a static language's rigidity redundant.
By the way, one way to check your test suites is "error injection": systematically go over your codebase introducing one mis-spelling -- run the tests to make sure they do fail, if they don't add one that does fail, correct the spelling mistake, repeat. Can be fairly well automated (not the "add a test" part, but the finding of potential errors that aren't covered by the suite), as can some other forms of error injections (change every integer constant, one by one, to one more, and to one less; change each <
to <=
etc; swap each if
and while
condition to its reverse; ...), while other forms of error-injection yet require a lot more human savvy. Unfortunately I don't know of publicly available suites of error injection frameworks (for any language) -- might make a cool open source project;-).