1
\$\begingroup\$

Readability counts, but is very subjective. The following snippets are equivalent in functionality and turn a generator iterator into a chunked HTTP response in Flask. Which of those two patterns is more Pythonic, readable and convenient?

A) Aspect style with function decorators that change the return value:

@app.route('/')
@flask_response_decorator
@optional_streaming_decorator
@progress_log_decorator
def index() -> Iterator:
    """Index page, optionally HTTP chunk-streamed and formatted."""
    return Foo.get_instance().are_changes_locked()

or

B) explicit decorators within the function body

@app.route('/')
def index() -> flask.Response:
    """Index page, optionally HTTP chunk-streamed and formatted."""
    response = Foo.get_instance().are_changes_locked()
    return flask.Response(optional_iterator_to_stream(iterator_to_progress_log(response)))
\$\endgroup\$
  • \$\begingroup\$ Around here, we generally don't like brevity. \$\endgroup\$ – Stephen Rauch Jul 1 '19 at 2:20
  • \$\begingroup\$ I added docstrings. The code above is now exactly as it is in my project, except from the Foo class, which is irrelevant for this example. \$\endgroup\$ – Hubert Grzeskowiak Jul 1 '19 at 3:19
  • \$\begingroup\$ You say yourself that it is subjective, that IMHO means its not a good fit for the kind of "objective" QA-style system here. In addition, you did not provide a lot of code to judge the code/differences in a greater context, which makes this question tend even more to the off-topic side of things. \$\endgroup\$ – AlexV Jul 1 '19 at 6:11
  • \$\begingroup\$ I don't see how this question could possibly be off-topic. It is matched by the first point of on-topic examples: "If you have a working piece of code from your project and are looking for open-ended feedback in the following areas: Application of best practices and design pattern usage (...)". This question is about the usage of decorators for transforming the return value of a Flask view function and I provided a real piece of code I am using in production. \$\endgroup\$ – Hubert Grzeskowiak Jul 1 '19 at 6:57
1
\$\begingroup\$

Pythonic, readable and convenient

are all subjective and open to debate, even though "Pythonic" is less so than the others. That said, a simplified version may be illustrative. Comparing

@foo
@bar
def baz(value: T) -> T:
    return value

to

def baz(value: T) -> FooT:
    return foo(bar(value))

there are two issues with the former:

  1. The final return type is now hidden in foo.
  2. It is easy to confuse the layering - is baz(value) equivalent to foo(bar(baz(value))) or bar(foo(baz(value))) (or even baz(foo(bar(value))) in case of a novice)? Nested decorators come up so rarely that I would have to look at the documentation to be absolutely sure whether they were in the right sequence.

There seems to be two situations where multi-level decorators would be fine:

  1. If they pass through the return value unchanged. A logger would be a typical example.
  2. If the decorators are commutative, that is, their order doesn't matter for the outcome.
| improve this answer | |
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.