advice about `correct' use of decorator

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Fri Aug 24 00:06:33 EDT 2007


En Thu, 23 Aug 2007 09:20:21 -0300, BJörn Lindqvist <bjourne at gmail.com>  
escribi�:

> On 8/22/07, Gabriel Genellina <gagsl-py2 at yahoo.com.ar> wrote:
>> On 22 ago, 10:00, "BJörn Lindqvist" <bjou... at gmail.com> wrote:
>> > As I said, you can accomplish the exact same thing by calling a
>> > function from within the function that requires the user to be logged
>> > in.
>> >
>> > def change_pass():
>> >     check_user_logged_in()
>> >     ... more stuff here...
>> >
>> > is less complex (and therefore preferable) than:
>> >
>> > @check_user_logged_in
>> > def change_pass():
>> >     ... more stuff here...
>> >
>> > An important principle in engineering is that you should always strive
>> > to make your design as simple as possible. If you have two equal
>> > designs, you should always choose the one that is simpler because
>> > simple things are easier to understand than complex things.
>>
>> I don't see the complexity in this case - both are a single line of
>> code. Any internal complexity is hidden by the language. In fact, I
>
> "Hiding" doesn't reduce complexity, quite the opposite.


>> consider the decorated function simpler than the other: its body
>> represents exactly what the function does, without any distracting
>> precondition calls.
>
> Think about how the decorator is implemented. It is a high order
> function, taking a function and returning a new function. Something
> like this:
>
> 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. Coupling and cohesion are some of its main attributes.  
I'm *not* talking about "algorithmic complexity", by example; nor any  
other aspect of complexity.
Given this frame, I think you may understand why "hiding details" reduces  
complexity: the programmer does not have to think/worry about those  
particular items. 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.

Let's look at a generator. Internally it's a complex thing: it has to keep  
its execution state, the stack frames, local and global variables... But  
all that internal complexity is hidden by the language, and the Python  
programmer only sees a continuable function. And that's great. Suppose you  
have a producer bound to a consumer - they are tightly coupled and that  
makes the code too complex. Using a generator you can decouple consumer  
 from producer, making the code a lot simpler. What's important is how the  
programmer sees it; it doesn't matter if the underlying structures and  
tools were hard to program; it does matter if *now*, using those  
structures and tools, the programmer can write simpler and more  
comprehensible code (and faster, and less likely to contain bugs).

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.

>> The decorator has some advantages: can have syntax support on your
>> editor, can perform some registering/logging, it's even easier to
>> quickly check visually if it's here.
>
> The decorator has just as many disadvantages. For example, what if you
> want to redirect back to the change_pass page once the user has
> pressed the Login button on the login_page? Or if you want to show
> different login pages depending on user properties? How much control
> flow do you really want to "hide" in your decorator?

I can't see the disadvantages; using a decorator, you have available the  
function being decorated, and you can manipulate it as you wish.

-- 
Gabriel Genellina




More information about the Python-list mailing list