[Python-Dev] PEP 318: Decorators last before colon

Jack Diederich jack at performancedrivers.com
Wed Mar 31 10:53:21 EST 2004


My use cases are at the bottom, but a couple comments first

On Tue, Mar 30, 2004 at 02:58:41PM -0800, Guido van Rossum wrote:
> > The most obvious issues I see with this is are that:
> >   - grepping for "def func" won't show any sign that its decorated, and 
> 
> Seems rather minor -- there's only so much you can cram on a single
> line.
see below, in my app I would never hit the 80 char mark w/ inline decorators.


> > Neither of these are fatal flaws, but I think I still prefer:
> > 
> >     def func(args) [decorator]:
> 
> Which hides 'classmethod' behind the more voluminous stuff, and that's
> my main gripe.

I don't think 'classmethod' is more important than the function def,
def func(cls) [staticmethod]: pass
is obviously wrong by convention and very easy to spot by eye or with 
a future version of pychecker.

def [classmethod] func(cls): pass
I read this as redundant, and grepping (eye and machine) just got much harder.

Here is my use case, this is a production application written in python.
It isn't a framework so there aren't many instances of decorators.

27k lines of python
371 classes
1381 functions (includes class and module functions)

function decorators,
  staticmethod 12
  classmethod 4
  memoize 1

class decorators
  Factory.register 50
  run_nightly 45

Here are some typical use cases out of that set done in my preferred style
of decorators last.

-- begin samples --

# staticmethods are almost always factory methods
class Contact(Entity):
  def new_by_email(email) [staticmethod]: pass
class CollectFactory(object):
  def class_ob(db_id, form_id) [staticmethod]: pass

# but sometimes helpers in a namespace
class Db(object):
  def client_database_name(client_id) [staticmethod]:
    return 'enduser_%d' % (client_id)

# classmethods are a mixed bag
class DataSet(object):
  table_name = 'foo'
  def drop_table(cls) [classmethod]: pass
  def create_table(cls) [classmethod]: pass

class Collect(object):
  def validate_field_trans(cls) [classmethod]: pass
  """this is run in the test suite, it just makes sure any derivative 
     classes are sane
  """

# and one memoize
cache_here = {}
def days_of_week(day_in_week) [memoize(cache_here)]:
  """for the passed in day, figure out what YEARWEEK it is and return a list 
     of all the days in that week.
  """

# the class decorators all register with a factory of
# their appropriate type.  I currently do this with metaclasses
class NewUsers(DataSet) [run_nightly]: pass # 45 of these

# note, not all DataSet derivatives run_nightly, the current way
# I get around this while using metaclasses is
class NewUsers(DataSet):
  run_nightly = 0 # run_nightly, 1/4 of DataSets set this to false
    
CF = CollectFactory
class Newsletter(Collect) [CF.register(db=4, form_id=3)]: pass # about 50 of these
# see CollectFactory.class_ob() above

-- end samples --

There are still some spots where I will continue to use metaclasses
instead of class decorators, namely where I want to keep track of all the leaf
classes.  But usually I want to decorate classes on a case-by-case basis.

These numbers are only representative of one application.
SPARK or other framework type things are going to use decorators a lot
more pervasively.  The number of people writing applications has to
be much higher than the number of people writing frameworks.

I'll use decorators whatever they look like, but they aren't more important to
me than the function definition.  And I don't want to have to relearn how to
grep for them.  So I prefer

def foo(self, bar) [decorator1]: pass

In my 27 tousand lines of python this will ALWAYS fit on one line and it
won't change how I eye-grep or machine-grep for functions.

-jackdied, BUFL (Benign User For Life)





More information about the Python-Dev mailing list