python/peps
| 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: 29-Oct-2011 | |
| Python-Version: 3.3 | |
| Post-History: | |
| Resolution: https://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 classes, 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: |