Source

python-peps / pep-3155.txt

PEP: 3155
Title: Qualified name for classes and functions
Version: $Revision$
Last-Modified: $Date$
Author: Antoine Pitrou <solipsis@pitrou.net>
Status: Final
Type: Standards Track
Content-Type: text/x-rst
Created: 2011-10-29
Python-Version: 3.3
Post-History:
Resolution: http://mail.python.org/pipermail/python-dev/2011-November/114545.html


Rationale
=========

Python's introspection facilities have long had poor support for
nested classes.  Given a class object, it is impossible to know
whether it was defined inside another class or at module top-level;
and, if the former, it is also impossible to know in which class it
was defined.  While use of nested classes is often considered poor
style, the only reason for them to have second class introspection
support is a lousy pun.

Python 3 adds insult to injury by dropping what was formerly known as
unbound methods.  In Python 2, given the following definition::

    class C:
        def f():
            pass

you can then walk up from the ``C.f`` object to its defining class::

    >>> C.f.im_class
    <class '__main__.C'>

This possibility is gone in Python 3::

    >>> C.f.im_class
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'function' object has no attribute 'im_class'
    >>> dir(C.f)
    ['__annotations__', '__call__', '__class__', '__closure__', '__code__',
    '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__',
    '__eq__', '__format__', '__ge__', '__get__', '__getattribute__',
    '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__',
    '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__']

This limits again the introspection capabilities available to the
user.  It can produce actual issues when porting software to Python 3,
for example Twisted Core where the issue of introspecting method
objects came up several times.  It also limits pickling support [1]_.


Proposal
========

This PEP proposes the addition of a ``__qualname__`` attribute to
functions and classes.  For top-level functions and classes, the
``__qualname__`` attribute is equal to the ``__name__`` attribute.  For
nested classed, methods, and nested functions, the ``__qualname__``
attribute contains a dotted path leading to the object from the module
top-level.  A function's local namespace is represented in that dotted
path by a component named ``<locals>``.

The repr() and str() of functions and classes is modified to use
``__qualname__`` rather than ``__name__``.

Example with nested classes
---------------------------

>>> class C:
...   def f(): pass
...   class D:
...     def g(): pass
...
>>> C.__qualname__
'C'
>>> C.f.__qualname__
'C.f'
>>> C.D.__qualname__
'C.D'
>>> C.D.g.__qualname__
'C.D.g'

Example with nested functions
-----------------------------

>>> def f():
...   def g(): pass
...   return g
...
>>> f.__qualname__
'f'
>>> f().__qualname__
'f.<locals>.g'


Limitations
===========

With nested functions (and classes defined inside functions), the
dotted path will not be walkable programmatically as a function's
namespace is not available from the outside.  It will still be more
helpful to the human reader than the bare ``__name__``.

As the ``__name__`` attribute, the ``__qualname__`` attribute is computed
statically and it will not automatically follow rebinding.


Discussion
==========

Excluding the module name
-------------------------

As ``__name__``, ``__qualname__`` doesn't include the module name.  This
makes it independent of module aliasing and rebinding, and also allows to
compute it at compile time.

Reviving unbound methods
------------------------

Reviving unbound methods would only solve a fraction of the problems this
PEP solves, at a higher price (an additional object type and an additional
indirection, rather than an additional attribute).


Naming choice
=============

"Qualified name" is the best approximation, as a short phrase, of what the
additional attribute is about.  It is not a "full name" or "fully qualified
name" since it (deliberately) does not include the module name.  Calling
it a "path" would risk confusion with filesystem paths and the ``__file__``
attribute.

The first proposal for the attribute name was to call it ``__qname__`` but
many people (who are not aware of previous use of such jargon in e.g. the
XML specification [2]_) found it obscure and non-obvious, which is why the
slightly less short and more explicit ``__qualname__`` was finally chosen.


References
==========

.. [1] "pickle should support methods":
   http://bugs.python.org/issue9276

.. [2] "QName" entry in Wikipedia:
   http://en.wikipedia.org/wiki/QName


Copyright
=========

This document has been placed in the public domain.



..
   Local Variables:
   mode: indented-text
   indent-tabs-mode: nil
   sentence-end-double-space: t
   fill-column: 70
   coding: utf-8
   End:
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.