Are decorators really that different from metaclasses...

Bengt Richter bokr at oz.net
Fri Aug 27 23:03:39 EDT 2004


On Fri, 27 Aug 2004 18:39:51 -0400, Paul Morrow <pm_mon at yahoo.com> wrote:

>Anthony Baxter wrote:
>> On Thu, 26 Aug 2004 16:09:42 -0400, Paul Morrow <pm_mon at yahoo.com> wrote:
>> 
>>>Yes, it doesn't seem all that complex, although I'm not sure that
>>>everyone reading this understands them and their subtleties.  The
>>>following is an excerpt from
>>>http://docs.python.org/tut/node11.html#SECTION0011200000000000000000
>>>
>>>"A namespace is a mapping from names to objects. Most namespaces are
>>>currently implemented as Python dictionaries, but that's normally not
>>>noticeable in any way (except for performance), and it may change in the
>>>future. Examples of namespaces are: the set of built-in names (functions
>>>such as abs(), and built-in exception names); the global names in a
>>>module; and the local names in a function invocation. In a sense the set
>>>of attributes of an object also form a namespace."
>> 
>> 
>>>When I talk about namespaces, I include all of the above, including the
>>>sense mentioned in the last line.  So an object's attributes constitute
>>>a namespace too.  Therefore __doc__, being an attribute of the function
>>>object, is in the function object's /namespace/.  And note that this is
>>>*not* a new namespace; it's been there all along.
>> 
>> 
>> "In a sense" is the bit you're missing here. You can't just hand-wave
>> and say that it's a namespace. It's *not* a namespace. If it was, you
>> could do any of these:
>> 
>
>No I didn't miss it (I read that bit).  In a sense, every object has 
>it's own namespace.  So that means that every class, every module, every 
>function has a namespace (in a sense).  The namespace (the mapping from 
>names to objects) is stored in each object's special __dict__ attribute.
>
>Ok, and this is certainly apparent with classes.
>
>   >>> class Foo:
>   ... 	"""docstring for Foo"""
>   ...
>   >>> Foo.__doc__
>   'docstring for Foo'
>   >>> Foo.__dict__.keys()
>   ['__module__', '__doc__']
>   >>>
>
>And because of the parallels between the above class definition of Foo 
>and the following function definition of baz, I would expect the same 
>behavior.
>
>   >>> def baz():
>   ...    """docstring of baz"""
>   ...
>   >>> baz.__doc__
>   'docstring of baz'
>   >>> baz.__dict__.keys()
>   []
>
>Say what?  Why didn't the Python system put baz's docstring into it's 
>namespace (__dict__)?   And where did it put it?
>
>I would like to understand the answers to these questions.  Can you 
>answer them (will you)?  If not, can you please point me at something 
>that documents what's going on here?
>
>Thanks.
>
It looks to me like __doc__ is a descriptor (or c-code that acts like one)
(i.e., like a property) of the function class:

 >>> def foo():
 ...     'foo doc string'
 ...

Find __doc__ in the class dict:
 >>> type(foo).__dict__['__doc__']
 <member '__doc__' of 'function' objects>

Check on its nature a little:
 >>> type(type(foo).__dict__['__doc__'])
 <type 'member_descriptor'>

It should have a __get__ method
 >>> type(foo).__dict__['__doc__'].__get__
 <method-wrapper object at 0x00901410>

Call that with foo as object instance:
 >>> type(foo).__dict__['__doc__'].__get__(foo,None)
 'foo doc string'

Try writing the __doc__ property (check for __set__ first):
 >>> type(foo).__dict__['__doc__'].__set__
 <method-wrapper object at 0x00901490>

Do long version of foo.__doc__ = 'new docstring'
 >>> type(foo).__dict__['__doc__'].__set__(foo, 'new docstring') # foo.__doc__ = ...

Look at it using the short spelling:
 >>> foo.__doc__
 'new docstring'

And the long:
 >>> type(foo).__dict__['__doc__'].__get__(foo,None)
 'new docstring'

foo.func_doc seems to be implemented in a similar way:
 >>> type(foo).__dict__['func_doc'].__get__(foo,None)
 'new docstring'
 >>> foo.func_doc
 'new docstring'

Even the dict of a function instance appears to be implemented as a property:
 >>> type(type(foo).__dict__['__dict__'])
 <type 'getset_descriptor'>
 >>> type(foo).__dict__['__dict__'].__get__(foo,None)
 {}
 >>> foo.x=123
 >>> type(foo).__dict__['__dict__'].__get__(foo,None)
 {'x': 123}

Short spelling:
 >>> foo.__dict__
 {'x': 123}

I haven't been into the implementation code, but this interpretation
of surface appearances seems to fit.

Regards,
Bengt Richter



More information about the Python-list mailing list