Are decorators really that different from metaclasses...

Hallvard B Furuseth h.b.furuseth at usit.uio.no
Sun Sep 5 21:36:07 EDT 2004


Paul Morrow wrote:
>Hallvard B Furuseth wrote:
>>
>> (...)  The difference is that magic variables in this
>> particular localtion is actually parsed differently from use of
>> variables other places, so the '=' operator (and what other operators?)
>> means something else.
> 
> Only the '=' operator, no others.

OK.

>>Paul Morrow wrote:
>>>The double underscores before and after each name loudly proclaims
>>>that these variables are not like 'normal' variables.  They're special
>>>in some way.
>> 
>> No other magic variables are that special.  They all behave normally _as
>> variables_.  Other parts of Python react to them being set or reset, the
>> very variable assigments are quite normal.  Except for the name mangling
>> of __*__ instance variables, but that is a lot smaller exception than
>> changing the namespace of the assignment operator and the time at which
>> that operator is executed.
> 
> Assignments that pertain to the definition of something are processed
> when the thing is defined.  When used as I've been describing here,
> __xxx__ variables pertain to the definition of the function.  (...)

Yes, that's my point.  Other magic variables are not so magical that
they change how the code is parsed.

>> (...) while it may seem intuitive
>> to you, you may have noticed that it seems counterintuitive to a lot of
>> others.
> 
> I have no idea how the lurkers feel about this.  Maybe you are right.
> Maybe this only makes sense to me.

Well, I note you have gained at least one vote:-)

>>>and 2) this style has parallels in both class definitions and 
>>>module definitions.
>> 
>> It parallels them as long as you don't think of how it works.  Once you
>> do think of how it works, it's completely different.  And you get a lot
>> of subtle differences to confuse the issue.
> 
> You need to show me the subtle differences.  I'm not aware of any (that
> matter).

I have no idea what differences matters to whom, but:

One common thing I've seen is people playing with __dict__:

    def   f(): """foo"""
    class c:   """foo"""
    print "function doc, dict:", f.__doc__, f.__dict__
    print "class    doc, dict:", c.__doc__, c.__dict__
-->
    function doc, dict: foo {}
    class    doc, dict: foo {'__module__': '__main__', '__doc__': 'foo'}

  c's __doc__ can be retrieved from __dict__, while f's __doc__ cannot.

Another:

    def f():
      __foo__ = something()
      __bar__ = map(lambda x: __foo__ * x, something_else())
      pass

  I expect __foo__ would be visible to the lambda.  It is not if you
  replace 'def f():' with 'class c:'.

One which I thought would be different, but it turned out
it is not:

    x = 'global'
    def f():
      __foo__ = x  # __foo__ = 'global'
      x = 'local'

  I expected this to complain that local x is used before assigned
  similar to in a function body, but it used the global x:

    x = 'global'
    class c:
      __foo__ = x
      x = 'local'

>>>Compare this class statement with the function def 
>>>above.
>>>
>>>     class Oval(Circle):
>>>         """This describe's Oval."""
>>>         __author__ = 'Paul Morrow'
>>>         __version__ = '0.1'
>>>         __metaclass__ = M
>> 
>> Yes, I can compare that.  It's fine in a class body, which is executed
>> when the class definition is executed.  When I see it in a function
>> definition, I see a strange use of the assignment operator, which is
>> normally a 'run-time' operator, in a place where it not being executed
>> at run-time.
> 
> It's also a 'definition-time' operator,

Not in my eyes.  See below.

> as it often appears in class and module definitions.  So why not be
> consistent and let it appear in function definitions too?

Because function definitions are inconsistent with class/module
definitions anyway in that respect.  Function bodies are not executed
when the function is defined, unlike class/module bodies.

In my eyes, it's not the assignment operator which is 'also a
definition-time operator', it's class/module definition which is
'run-time operators'.  Or something like that, anyway.  If you call
assignment a 'definition-time' operator just because it can be used
while defining classes and modules, then anything is a 'definition-
time' operator.

If so, I'll ask you for some other word you'll accept for normal
execution of statements, including in class/module bodies, as opposed to
seeing a function definition and defining the function but not executing
its body, and then I'll ask you to replace 'run-time' with that other
word in the paragraph you quoted above.

> What is so outrageous about this idea?

There is nothing outrageous about splitting function definitions in one
part which is executed when the definition is seen and one when the
function is run.  That's more or less what decorators are about, after
all.  It's your syntax for it which I'm against.

> On the 'strange-meter', does it rank as high as docstrings or
> meaningful indentation?

Yes, I think it's quite strange to not make a clearer distinction
between statements which are executed at different times and in
different namespaces.  Docstrings never registered on my 'strange-meter'
at all.  Indentation did, the first week or two.  Both are quite clear
even to someone who doesn't know Python, while your syntax would at the
very least lead a newcomer to assume the function attributes are visible
in the function body.  I think nearly every proposed decorator syntax
was less misleading.

>>>Maybe we just need to be clear as to which lines under the def statement
>>>constitute the function's /body/.
>> 
>> That's perfectly clear without your change.  Your change is confusing
>> the issue, by also using an operator which one (well, at least many of
>> us - not you, obviously) expects to be executed in the function body.
> 
> We change the definition of where the function body starts, to exclude
> any __xxx__ assignments immediately following the docstring.  Then the
> '=' operator has definition-time semantics, not run-time.

True enough.

>> BTW, I was all for having the function body extend further down - my
>> first decorator choice (before J2) would have been the
>>   def foo(...):
>>      ...decorators and doc string...
>>   some_keyword:
>>      ...body...
>> 
>> syntax.  Like you point out about __*__ assigments it stands out, but
>> it's even clearer, but it doesn't look like it would normally
>> dosomething else, so the uninitiated will have far less reason to wonder
>> what kind of voodoo is going on to make it do what it does.
> 
> All of the suggestions I've seen like that one have the section keyword
> at the same level of indentation as the def keyword, which is wrong
> (IMO).  There is no precedent for that in Python.  We are defining
> something; every statement related to that definition should be indented
> farther to the right than the def statement (IMO).

Heh.  That argument against that syntax had not occurred to me.  I just
think of it as similar to try:/except:.  It seems clear to me.  But
then, there wasn't any precedent in Python for your way of having no
indentation between statements that are executed at different times
either.  Not until we got @decorators, anyway.

-- 
Hallvard



More information about the Python-list mailing list