Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #29209

Re: Decorators not worth the effort

Path csiph.com!newsfeed.hal-mli.net!feeder3.hal-mli.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!newsfeed.xs4all.nl!newsfeed6.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail
Return-Path <dieter@handshake.de>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'python.': 0.02; 'else:': 0.03; 'example:': 0.03; 'cache': 0.05; '*args,': 0.07; 'decorator': 0.07; 'defines': 0.07; 'definitions': 0.07; 'function,': 0.07; 'soap': 0.07; 'used.': 0.07; 'python': 0.09; 'caching,': 0.09; 'item,': 0.09; 'logic': 0.09; 'sure,': 0.09; 'way:': 0.09; 'cc:addr:python-list': 0.10; 'def': 0.10; 'itself.': 0.11; 'subject:not': 0.11; 'suggest': 0.11; ':-)': 0.13; 'cases': 0.15; 'value.': 0.15; '(important': 0.16; '>to': 0.16; 'caching': 0.16; 'content-type': 0.16; 'error)': 0.16; 'false):': 0.16; 'item):': 0.16; 'non-trivial': 0.16; 'preserving': 0.16; 'redundancy': 0.16; 'reproduce': 0.16; 'skip:@ 20': 0.16; 'skip:{ 30': 0.16; 'syntactic': 0.16; 'task.': 0.16; 'copied': 0.17; 'module,': 0.17; 'examples': 0.18; 'module': 0.19; 'parameters': 0.20; 'putting': 0.20; 'not,': 0.21; '(by': 0.22; 'are.': 0.22; 'aspect': 0.22; 'decorators': 0.22; 'oriented': 0.22; 'tells': 0.22; 'cc:2**0': 0.23; 'example': 0.23; 'work.': 0.23; "python's": 0.23; 'specified': 0.23; 'task': 0.23; 'this:': 0.23; 'project,': 0.24; 'cc:no real name:2**0': 0.24; 'idea': 0.24; 'second': 0.24; 'header': 0.24; 'least': 0.25; 'cc:addr:python.org': 0.25; 'header :In-Reply-To:1': 0.25; 'looks': 0.26; 'wrote': 0.26; 'values': 0.26; 'implemented': 0.27; 'logging': 0.27; 'separate': 0.27; 'skip:# 10': 0.27; '(as': 0.27; 'skip:@ 10': 0.27; 'correct': 0.28; 'lines': 0.28; 'initial': 0.28; 'represent': 0.28; '"do': 0.29; 'convince': 0.29; 'improves': 0.29; 'key,': 0.29; 'obscure': 0.29; 'thus,': 0.29; 'definition': 0.29; "skip:' 10": 0.30; 'function': 0.30; 'primary': 0.30; 'code': 0.31; 'implement': 0.32; 'could': 0.32; 'info': 0.32; 'skip:s 30': 0.33; 'function.': 0.33; 'like:': 0.33; 'much.': 0.33; 'another': 0.33; 'changed': 0.34; 'thanks': 0.34; 'adds': 0.35; 'false': 0.35; 'programming.': 0.35; 'sometimes': 0.35; 'there': 0.35; 'add': 0.36; 'really': 0.36; 'but': 0.36; 'depends': 0.36; 'skip:{ 10': 0.36; 'should': 0.36; 'charset:us-ascii': 0.36; 'problems': 0.36; 'received:88.198': 0.37; 'signature': 0.37; 'uses': 0.37; 'why': 0.37; '(for': 0.37; 'skip:v 20': 0.37; 'data': 0.37; 'subject:: ': 0.38; 'some': 0.38; 'instead': 0.39; 'where': 0.40; 'skip:" 10': 0.40; 'end': 0.40; 'your': 0.60; 'easy': 0.60; 'most': 0.61; 'further': 0.61; 'improved': 0.62; 'provide': 0.62; 'different': 0.63; 'details': 0.63; 'more': 0.63; 'services': 0.64; 'useful.': 0.65; 'finally': 0.66; 'protect': 0.69; 'as:': 0.75; 'message- id:@localhost.localdomain': 0.75; 'gain': 0.79; 'customer,': 0.84; 'facilitating': 0.84; 'firewalls': 0.84; 'isolated': 0.84; 'responses': 0.93; 'serious': 0.98
MIME-Version 1.0
Content-Type text/plain; charset=us-ascii
Content-Transfer-Encoding 7bit
Date Sat, 15 Sep 2012 08:31:30 +0200
From "Dieter Maurer" <dieter@handshake.de>
To Dwight Hutto <dwightdhutto@gmail.com>
Subject Re: Decorators not worth the effort
In-Reply-To <CA+vVgJVuPW6ygW6adutp38m27pzfLGMNHhg-0nVS8EPngHV3Mw@mail.gmail.com>
References <mailman.623.1347558870.27098.python-list@python.org> <f7e2ec78-2c4a-402e-9a90-6cf0e7e55a54@c8g2000pbe.googlegroups.com> <874nn1jedk.fsf@handshake.de> <CA+vVgJVuPW6ygW6adutp38m27pzfLGMNHhg-0nVS8EPngHV3Mw@mail.gmail.com>
X-Mailer VM 8.0.12-devo-585 under 21.4 (patch 22) "Instant Classic" XEmacs Lucid (i686-linux-gnu)
Cc python-list@python.org
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.15
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <http://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <http://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Newsgroups comp.lang.python
Message-ID <mailman.738.1347693346.27098.python-list@python.org> (permalink)
Lines 150
NNTP-Posting-Host 2001:888:2000:d::a6
X-Trace 1347693346 news.xs4all.nl 6893 [2001:888:2000:d::a6]:44273
X-Complaints-To abuse@xs4all.nl
Xref csiph.com comp.lang.python:29209

Show key headers only | View raw


Dwight Hutto wrote at 2012-9-14 23:42 -0400:
> ...
>Reduce redundancy, is argumentative.
>
>To me, a decorator, is no more than a logging function. Correct me if
>I'm wrong.

Well, it depends on how you are using decorators and how complex
your decorators are. If what you are using as decorating function
it really trivial, as trivial as "@<decoratorname>", then you
do not gain much.

But your decorator functions need not be trivial.
An example: in a recent project,
I have implemented a SOAP webservice where most services depend
on a valid session and must return specified fields even when
(as in the case of an error) there is no senseful value.
Instead of putting into each of those function implementations
the check "do I have a valid session?" and at the end
"add required fields not specified", I opted for the following
decorator:

def valid_session(*fields):
! fields = ("errorcode",) + fields
  @decorator
  def valid_session(f, self, sessionkey, *args, **kw):
!   s = get_session(sessionkey)
!   if not s.get("authenticated", False):
!     rd = {"errorcode": u"1000"}
!   else:
!     rd = f(self, sessionkey, *args, **kw)
!   return tuple(rd.get(field, DEFAULTS.get(field, '')) for field in fields)
  return valid_session

The lines starting with "!" represent the logic encapsulated by the
decorator -- the logic, I would have to copy into each function implementation
without it.

I then use it this way:

  @valid_session()
  def logout(self, sessionkey):
    s = get_session(sessionkey)
    s["authenticated"] = False
    return {}

  @valid_session("amountavail")
  def getStock(self, sessionkey, customer, item, amount):
    info = self._get_article(item)
    return {u"amountavail":info["deliverability"] and u"0" or u"1"}

  @valid_session("item", "shortdescription", "pe", "me", "min", "price", "vpe", "stock", "linkpicture", "linkdetail", "linklist", "description", "tax")
  def fetchDetail(self, sessionkey, customer, item):
    return self._get_article(item)
  ...

I hope you can see that at least in this example, the use of the decorator
reduces redundancy and highly improves readability -- because
boilerplate code (check valid session, add default values for unspecified
fields) is not copied over and over again but isolated in a single place.


The example uses a second decorator ("@decorator") --
in the decorator definition itself. This decorator comes from the
"decorator" module, a module facilitating the definition of signature
preserving decorators (important in my context): such a decorator
ensures that the decoration result has the same parameters as the
decorated function. To achieve this, complex Python implementation
details and Python's introspection must be used. And I am very
happy that I do not have to reproduce this logic in my decorator
definitions but just say "@decorator" :-)


Example 3: In another project, I had to implement a webservice
where most of the functions should return "json" serialized data
structures. As I like decorators, I chose a "@json" decorator.
Its definition looks like this:

@decorator
def json(f, self, *args, **kw):
  r = f(self, *args, **kw)
  self.request.response.setHeader(
    'content-type',
    # "application/json" made problems with the firewall,
    #  try "text/json" instead
    #'application/json; charset=utf-8'
    'text/json; charset=utf-8'
    )
  return udumps(r)

It calls the decorated function, then adds the correct "content-type"
header and finally returns the "json" serialized return value.

The webservice function definitions then look like:

    @json
    def f1(self, ....):
       ....

    @json
    def f2(self, ...):
       ....

The function implementions can concentrate on their primary task.
The "json" decorator" tells that the result is (by magic specified
elsewhere) turned into a "json" serialized value.

This example demontrates the improved maintainability (caused by
the redundancy reduction): the "json rpc" specification stipulates
the use of the "application/json" content type. Correspondingly,
I used this content-type header initially. However, many enterprise
firewalls try to protect against viruses by banning "application/*"
responses -- and in those environments, my initial webservice
implementation did not work. Thus, I changed the content type
to "text/json". Thanks to the decorator encapsulation of the
"json result logic", I could make my change at a single place -- not littered
all over the webservice implementation.


And a final example: Sometimes you are interested to cache (expensive)
function results. Caching involves non-trivial logic (determine the cache,
determine the key, check whether the cache contains a value for the key;
if not, call the function, cache the result). The package "plone.memoize"
defines a set of decorators (for different caching policies) which
which caching can be as easy as:

      @memoize
      def f(....):
          ....

The complete caching logic is encapsulated in the tiny "@memoize" prefix.
It tells: calls to this function are cached. The function implementation
can concentrate on its primary task and there is no need to obscure
the implementation by the orthogonal aspect of caching.


I hope I could convince you that while you may not have a serious need
for decorators, there are cases where they can be really useful.

Should I have not yet succeeded, I suggest you read some overview
on aspect oriented programming. I am sure, you will find there
losts of further examples why it is a good idea to separate
general purpose aspects (logging, monitoring, persistency, resource
management, caching, serialization, ...) from the primary task of
a function. Decorators provide syntactic sugur to facilitate this
separation in Python.


--
Dieter

Back to comp.lang.python | Previous | NextPrevious in thread | Find similar | Unroll thread


Thread

Re: Python presentations Jean-Michel Pichavant <jeanmichel@sequans.com> - 2012-09-13 19:54 +0200
  Re: Python presentations Alister <alister.ware@ntlworld.com> - 2012-09-13 18:18 +0000
  Decorators not worth the effort alex23 <wuwei23@gmail.com> - 2012-09-13 18:58 -0700
    Re: Decorators not worth the effort Cameron Simpson <cs@zip.com.au> - 2012-09-14 12:12 +1000
      Re: Decorators not worth the effort alex23 <wuwei23@gmail.com> - 2012-09-13 20:25 -0700
    Re: Decorators not worth the effort Dieter Maurer <dieter@handshake.de> - 2012-09-14 08:40 +0200
    Re: Decorators not worth the effort Terry Reedy <tjreedy@udel.edu> - 2012-09-14 16:29 -0400
      Re: Decorators not worth the effort Thomas Rachel <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915@spamschutz.glglgl.de> - 2012-09-15 09:13 +0200
      Re: Decorators not worth the effort alex23 <wuwei23@gmail.com> - 2012-09-16 17:38 -0700
    Re: Decorators not worth the effort Terry Reedy <tjreedy@udel.edu> - 2012-09-14 16:31 -0400
    Re: Decorators not worth the effort Terry Reedy <tjreedy@udel.edu> - 2012-09-14 16:37 -0400
      Re: Decorators not worth the effort 88888 Dihedral <dihedral88888@googlemail.com> - 2012-09-18 16:47 -0700
      Re: Decorators not worth the effort 88888 Dihedral <dihedral88888@googlemail.com> - 2012-09-18 16:47 -0700
    Re: Decorators not worth the effort Terry Reedy <tjreedy@udel.edu> - 2012-09-14 16:33 -0400
    Re: Decorators not worth the effort Ian Kelly <ian.g.kelly@gmail.com> - 2012-09-14 15:16 -0600
      Re: Decorators not worth the effort Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-14 23:39 +0000
        Re: Decorators not worth the effort 88888 Dihedral <dihedral88888@googlemail.com> - 2012-09-15 02:45 -0700
          Re: Decorators not worth the effort Dwight Hutto <dwightdhutto@gmail.com> - 2012-09-15 06:04 -0400
            Re: Decorators not worth the effort 88888 Dihedral <dihedral88888@googlemail.com> - 2012-09-15 07:18 -0700
              Re: Decorators not worth the effort Thomas Rachel <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915@spamschutz.glglgl.de> - 2012-09-18 14:16 +0200
            Re: Decorators not worth the effort 88888 Dihedral <dihedral88888@googlemail.com> - 2012-09-15 07:18 -0700
    Re: Decorators not worth the effort Dwight Hutto <dwightdhutto@gmail.com> - 2012-09-14 23:42 -0400
    Re: Decorators not worth the effort "Dieter Maurer" <dieter@handshake.de> - 2012-09-15 08:31 +0200

csiph-web