Use polymorphism and duck-typing before isinstance()
You generally define what you want to do with the shapes, then either use polymorphism to adjust how each shape responds to what you want to do, or you use duck typing; test if the shape at hand can do the thing you want to do in the first place. This is the invocation versus introspection trade-off, conventional wisdom states that invocation is preferable over introspection, but in Python, duck-typing is preferred over isinstance
testing.
So you need to work out why you need to filter on class in the first place; why do you need to find all the rectangles? Perhaps they are the only type that implements a certain method, in which case you use duck typing to test for that method instead.
Or perhaps you need to process each type in a different way, because each type has to be handled just right for whatever you are doing with them. In that case, you need to give each shape the same method, but implement it differently for each type, placing the responsibility for adaptation in each object.
ABCs
With duck-typing, you may find that you have to test for multiple methods, and thus a isinstance()
test may look a better option. In such cases, using a Abstract Base Class (ABC) could also be an option; using an ABC let's you 'paint' several different classes as being the right type for a given operation, for example. Using a ABC let's you focus on the tasks that need to be performed rather than the specific implementations used; you can have a Paintable
ABC, a Printable
ABC, etc.
Zope interfaces and component architecture
If you find your application is using an awful lot of ABCs or you keep having to add polymorphic methods to your classes to deal with various different situations, the next step is to consider using a full-blown component architecture, such as the Zope Component Architecture (ZCA).
zope.interface
interfaces are ABCs on steroids, especially when combined with the ZCA adapters. Interfaces document expected behaviour of a class:
if IRectangleShape.providedBy(yourshape):
# it's a rectangle
but it also let's you look up adapters; instead of putting all the behaviours for every use of shapes in your shape classes, you implement adapters to provide polymorphic behaviours for specific use-cases. You can adapt your shapes to be printable, or paintable, or exportable to XML:
class RectangleXMLExport(object):
adapts(IRectangleShape)
provides(IXMLExport)
def __init__(self, rectangle):
self.rectangle = rectangle
def export(self):
return u'<rectangle><width>{0}</width><height>{0}</height></rectangle>'.format(
self.rectangle.width, self.rectangle.height)
and your code merely has to look up adapters for each shape:
for shape in shapebag:
self.result.append(IXMLExport(shape).export())
Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?
If it is never thrown then it's not an exception. It is an object derived
from an Exception class, though it does not follow the behavior. Calling it an Exception is purely semantics at this point, but I see nothing wrong with not throwing it. From my perspective, not throwing an exception is the exact same thing as an inner-function catching it and preventing it from propagating.
Is it valid for a function to return exception objects?
Absolutely. Here is a short list of examples where this may be appropriate:
- An exception factory.
- A context object that reports if there was a previous error as a ready to use exception.
- A function that keeps a previously caught exception.
- A third-party API that creates an exception of an inner type.
Is not throwing it bad?
Throwing an exception is kind of like: "Go to jail. Do not pass Go!" in the board game Monopoly. It tells a compiler to jump over all source code up to the catch without executing any of that source code. It doesn't have anything to do with errors, reporting or stopping bugs. I like to think of throwing an exception as a "super return" statement for a function. It returns execution of the code to somewhere much higher up than the original caller.
The important thing here is to understand that the true value of exceptions is in the try/catch
pattern, and not the instantiation of the exception object. The exception object is just a state message.
In your question you seem to be confusing the usage of these two things: a jumping to a handler of the exception, and the error state the exception represents. Just because you took an error state and wrapped it in an exception does not mean you are following the try/catch
pattern or its benefits.
Best Answer
This code
Is called a Guard Clause, and it's a perfectly valid technique.
The reason you throw an exception here is that (presumably), if
codes
is not an instance of(list, tuple)
, then the method has no way to recover (unless you want to return some default value or error code from the method instead).