confusion with decorators

Jason Swails jason.swails at gmail.com
Thu Jan 31 22:13:30 EST 2013


On Thu, Jan 31, 2013 at 6:16 PM, Steven D'Aprano <
steve+comp.lang.python at pearwood.info> wrote:

>
> Normally, subclasses should extend functionality, not take it away. A
> fundamental principle of OO design is that anywhere you could sensibly
> allow an instance, should also be able to use a subclass.
>
> So if you have a Patch class, and a RemotePatch subclass, then everything
> that a Patch can do, a RemotePatch can do too, because RemotePatch
> instances *are also* instances of Patch.
>
> But the rule doesn't go in reverse: you can't necessarily use a Patch
> instance where you were using a RemotePatch. Subclasses are allowed to do
> *more*, but they shouldn't do *less*.
>
> On the other hand, if you have a Patch class, and a RemotePatchList class,
> inheritance does not seem to be the right relationship here. A
> RemotePatchList does not seem to be a kind of Patch, but a kind of list.
>
>
> > I'd imagine a much more sensible approach is to generate a base class
> that
> > implements all methods common to both and simply raises an exception in
> > those methods that aren't.  I agree it doesn't make much sense to inherit
> > from an object that has MORE functionality than you want.
>
> If a method is not common to both, it doesn't belong in the base class. The
> base should only include common methods.
>

Yes, I agree here.  The only reason I was considering NOT doing this was
because I wanted to control the exception that gets raised rather than let
through a simple NameError.  The reason, in case you care, is that I like
creating my own custom excepthook() which optionally suppresses tracebacks
of the base exception class of my program (which can be overridden by a
--debug option of some sort).

That way I don't worry about returning error codes and the like and my
exceptions double as error messages which don't scare users away.  Of
course, if I didn't raise the exception myself, then I definitely want to
know what line that error occurred on so I can fix it (since that typically
means it's a bug or error I did not handle gracefully).

I suppose I could get the same effect by dumping everything into a main()
function somewhere and wrapping that in a try/except where I catch my base
class, but I like the flexibility


> In fact, I'm usually rather suspicious of base classes that don't ever get
> used except as a base for subclassing. I'm not saying it's wrong, but it
> could be excessive abstraction. Abstraction is good, but you can have too
> much of a good thing. If the base class is not used, consider a flatter
> hierarchy:
>
>     class Patch:  ...
>     class RemotePatch(Patch):  ...
>
>
> rather than:
>
>     class PatchBase:  ...
>     class Patch(PatchBase):  ...
>     class RemotePatch(Patch):  ...
>
> although this is okay:
>
>     class PatchBase:  ...
>     class Patch(PatchBase):  ...
>     class RemotePatch(PatchBase):  ...
>

This last one is what I've settled on.  Patch and RemotePatch have common
functionality.  But RemotePatch can be downloaded and Patch can be parsed
through (in my app, if you're going to spend the time to parse through the
whole RemotePatch, it just gets downloaded and instantiated as a Patch).
 So this last form of inheritance made the most sense to me.


>
>
> > However, my desire to use decorators was not to disable methods in one
> > class vs. another.  The _protector_decorator (a name borrowed from my
> > actual code), is designed to wrap a function call inside a try/except, to
> > account for specific exceptions I might raise inside.
>
> Ah, your example looked like you were trying to implement some sort of
> access control, where some methods were flagged as "protected" to prevent
> subclasses from using them. Hence my quip about Java. What you describe
> here makes more sense.
>
>
> > One of my classes
> > deals with local file objects, and the other deals with remote file
> > objects
> > via urllib.  Naturally, the latter has other exceptions that can be
> > raised,
> > like HTTPError and the like.  So my desire was to override the decorator
> > to handle more types of exceptions, but leave the underlying methods
> > intact without duplicating them.
>
>     >>> decorated(3)
>     4
>
> One way to do that is to keep a list of exceptions to catch:
>
>
> class Patch:
>     catch_these = [SpamException, HamException]
>     def method(self, arg):
>         try:
>             do_this()
>         except self.catch_these:
>             do_that()
>
> The subclass can then extend or replace that list:
>
> class RemotePatch(Patch):
>     catch_these = Patch.catch_these + [EggsException, CheeseException]
>

Ha! I use this technique all the time to avoid code duplication (it's used
several times in the program I'm writing).  It didn't even occur to me in
this context... Thanks for pointing this out!

As always, the time you put into responses and helping is appreciated.

All the best,
Jason
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20130131/be56666f/attachment.html>


More information about the Python-list mailing list