However, one thing still doesn't sit right with me regarding this technique: a novice programmer may see "class" and think they need to instantiate it and pass the instance as the function/method argument. Using my previous example:
ApplyFlavor("blueberry", CurrentWidget)
- Class object passed as argument;
widget is CurrentWidget
so code that flavors the "current widget" is executed. ApplyFlavor("blueberry", CurrentWidget())
- Caller mistakenly passes instance as argument;
widget is not CurrentWidget
soApplyFlavor()
does not recognize the signal argument and runs code that accesses the widget object in ways that will probably throw exceptions.
This is nothing that a little documentation wouldn't fix, but I've yet to meet a junior programmer that actually reads all the documentation I wrote for them. So, being proactive, let's assume they don't read directions and catch their mistake for them:
from warnings import warn
class AllWidgets(object):
"""Placeholder for indicating action applies for all widgets
"""
def __new__(cls):
warn("%s should not be instantiated" % cls.__name__,
stacklevel=2)
return cls
....
def ApplyFlavor(flavor, widget=CurrentWidget):
if widget is CurrentWidget:
# Code that flavors the "current widget".
....
elif widget is AllWidgets:
# Code that flavors all widgets.
....
else:
# Code that flavors just the widget specified.
....
Now you cannot instantiate the class; trying to do so just returns an instance of the class object itself so
AllWidgets() is AllWidgets
.If you don't mind whether users erroneously try to instantiate the class, you can omit the warning. If, like me, you believe such errors are signs of misunderstanding that could lead to further bugs, issue a warning or -- if you are really zealous -- throw an exception from the
__new__()
method.
No comments:
Post a Comment