Brian,
I think you have done a great job of demonstrating that design has to be evaluated in the light of requirements. There are probably scenarios where each of these solutions makes sense. Without knowing how it is to be used, there is no way to pick the 'right' one.
For example in a bird simulation it might be fine to have Ostrich.fly() do nothing. Possibly the version that throws an exception would be useful there also. The class hierarchy with FlyingBird is maybe more 'correct' but I don't know that it would be any easier to use in practice.
If you make Ostrich.fly() raise AttributeError it will look to the caller like it is not implemented...NotImplementedError seems appropriate as well. If you want to define your own exception it can be as simple as
class FlightlessBirdException(Exception):
pass
Kent
Brian van den Broek wrote:
Hi all,
I am wondering about the Pythonic way to handle the problem of ostriches, emus, and penguins. (I cannot recall from where I got the example.)
Here's what I mean:
class Bird(object): def fly(self): # flying logic here def lay(self): # egg-laying logic here # more bird methods
class Ostrich(Bird): # ostriches can't fly, so what to do?
I've explored a number of solutions; here they are with what I see as to cons and problems:
The simplest thing is to emulate the ostrich and pretend the problem doesn't exist. But, putting one's head in the sand looks likely to cause troubles in that this route break encapsulation, requiring callers to know enough not to call the fly method of an ostrich. So, that's no good.
class Ostrich(Bird): def fly(self): pass
seems only marginally better, in that it gives the external appearance of flight, whereas what is needed is a "Hey, I don't fly" signal.
The next thought was to over-ride Ostrich.fly as def fly(self): raise NotImplementedError
That seems better, but also a bit confusing; the way I understand it, NotImplementedError is, in the first instance, for abstract classes or for marking work in progress. But Ostrich.fly doesn't fit either case.
That makes me think to define a custom exception, say
class OstrichError(NotImplementedError): '''A custom exception for cases of the "Ostrich problem".
Intended to be raised by methods in a subclass over-riding methods of the parent which don't make sense for the subclass to actually implement.''' def __init__(self): NotImplementedError.__init__(self)
But, since the problem isn't one that my penetrating genius discovered, I am inclined to think that were this the ideal solution, there'd be a (better named) exception class builtin to Python already.
A complicated class hierarchy like
class Bird(object): # bird logic
class FlyingBird(Bird): def fly(self): # flying logic here
class FlightlessBird(Bird): # any particularly flightless logic here
class Ostrich(FlightlessBird): # ostrich logic
seems an invitation to difficulty. My analogy will soon break, but some birds build nests and sing, others, no so much, etc. Flat is soon to give way to deeply nested.
I also tried to delete the inherited Bird.fly method within Ostrich.__init__, but
class Ostrich(Bird): def __init__(self): del self.__dict__['fly']
raises a KeyError, whereas
def __init__(self): del Ostrich.__dict__['fly']
raises: TypeError: object does not support item deletion
Do I have the syntax of the last approach wrong? Or is there no way to remove a method from a class? If the latter, what to do about flightless fowl?
Thanks and best,
Brian vdB
_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor
_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor