advice about `correct' use of decorator

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon Sep 3 02:38:35 EDT 2007


En Wed, 29 Aug 2007 07:32:21 -0300, BJörn Lindqvist <bjourne at gmail.com>  
escribi�:

> On 8/24/07, Gabriel Genellina <gagsl-py2 at yahoo.com.ar> wrote:
>> En Thu, 23 Aug 2007 09:20:21 -0300, BJörn Lindqvist <bjourne at gmail.com>
>> escribi�:
>>
>> > def check_user_logged_in(func):
>> >     def f(*args, **kwargs):
>> >         if global_state.the_user.is_logged_in:
>> >             return func(*args, **kwargs)
>> >         return show_login_page()
>> >     return f
>>
>> I think there is a semantic problem, perhaps we are not talking
>> about the same thing. I'm considering the software complexity AS
>> PERCEIVED BY THE PROGRAMMER, looking at the interactions between a
>> program and a programmer who is working on some task; some people
>> would say "cognitive complexity" to make it clear.
>
> There is no semantic problem. You are just mistaken in your belief
> that the complexity that the user of the decorator has to deal with is
> different from the complexity in implementing the decorator.

But they ARE different. That's the whole point of abstractions, code  
reuse, layered design... Designing a simple interfase on top of a complex  
system is very common. Nobody writes X-Window code, as nobody writes a raw  
event loop for Windows anymore: there are very good GUI toolkits, even  
portable frameworks, that put an abstract layer on top of that so the  
programmer has a much simple and coherent view. The complexity is behind  
the scenes.

>> Which API is more complex: one in which you can play a movie with
>> just a single call like PlayMovie(Title), or one on which you must
>> call a zillion functions to firtly initialize the stream format,
>> configure the display, disable undesired user interfase elements,
>> locate the movie file, load it in chunks, etc.? The first one is
>> certainly much simpler to use (simpler = less complex), and maybe
>> internally it calls the same functions as the second one, but nobody
>> cares.
>
> "nobody cares" is your guess. I'd bet that the caller of the PlayMovie
> function cares a lot: Is the movie played full screened? Which
> encodings are supported? Can you set the title of the movie window? Is
> streaming supported? Does it even work on windows? Which URI schemes
> does it support?  And so on and so on.
>
> That is why no video decoding API:s have a PlayMovie(Title) function
> and why I haven't seen a single 3d engine with a
> MakeReallyCoolDoomCloneFPSGame() function.

(yet!). What about urlopen? Using a single call one can be authenticated  
and retrieve any file in the other side of the planet. I consider it a  
simple interfase, altough it does complex things. Would you say it is  
better to use plain sockets everywhere? Or, since sockets are abstractions  
themselves, would you say it is better to use lower level primitives  
instead? Each time you descend a level, you have to use many simpler  
functions - but their combination is more complex.

> "hiding details" only works if the client programmer really doesn't
> care about the details.

Why should he care? Isn't "hiding implementation details" a good design  
principle?
When I use urlopen, I don't even care of the underlying socket  
implementation. I don't care which interfase the request is sent thru. I  
don't care if the answer got fragmented and some packets had to be  
reassembled. urlopen gives me something that looks like a file, and I just  
read() from it.

>> Back to your example, the fact that a decorator builds a higher order
>> function, does NOT make it more complex - because the programmer does  
>> not
>> see that. In fact, hiding the details makes it simpler.
>
> Yes, the programmer does see that. The example decorator I posted
> requires about a zillion preconditions to work correctly and will fail
> in weird ways when those preconditions are not satisfied. The
> programmer is interested in the crazy failures he or she will
> experience. I dare you to try and implementing the code both as a
> decorator and as a function, then write the unit tests and
> documentation. The complexity of those three items together
> (implementation + tests + documentation) will be much higher for the
> decorator choice because the complexity of the decorator
> implementation is a bit higher than using a plain old function.

Testing the decorator is as hard as testing any other function. Testing  
the decorated functions might involve *only* checking if the decorator is  
actually used for those functions.
Going to your posted example, I don't see the difference - I should say, I  
don't see the advantage. Using a decorator hides some implementation  
details and reduces coupling between modules, both good things on "my"  
book; your function with no decorator does quite the opposite, and doesn't  
look like good coding style (on "my" book, of course).

> Note also that it is extremely rare to see well-documented or
> well-tested code, which means that the programmer WILL have to analyze
> the implementation which means that implementation complexity matters
> a lot.

In any case, that would require testing and analyzing ONE function (the  
decorator) instead of inspecting a zillion repetitions of the same code  
that, when being wrong, have to be located and fixed on all those places.

Really, I still can't understand how you can defend such silly things -  
unless we are talking about different things.

-- 
Gabriel Genellina




More information about the Python-list mailing list