3

Please consider the following short Python 2.x script:

#!/usr/bin/env python
class A(object):
    class B(object):
        class C(object):
            pass
        def __init__(self):
            self.c = A.B.C()
    def __init__(self):
        self.b = A.B()

def main():
    a = A()
    print "%s: %r" % (type(a).__name__, type(a))
    print "%s: %r" % (type(a.b).__name__, type(a.b))
    print "%s: %r" % (type(a.b.c).__name__, type(a.b.c))

if __name__ == "__main__":
    main()

The output of which, when run in Python 2.7.6, is:

A: <class '__main__.A'>
B: <class '__main__.B'>
C: <class '__main__.C'>

I was expecting a different output here. Something more along the lines of:

A: <class '__main__.A'>
A.B: <class '__main__.A.B'>
A.B.C: <class '__main__.A.B.C'>

In particular I expected to see the same qualified name that I have to give to instantiate A.B and A.B.C classes respectively.

Could anyone shed any light on why those new type classes identify themselves as rooted in __main__ instead of how they were nested in the code?

Also: is there a way to fix this by naming the nested classes explicitly, such that they will identify themselves as A.B and A.B.C respectively (or possibly in the type representation as __main__.A.B and __main__.A.B.C respectively)?

1
  • @vaultah: thanks. Much appreciated. So there is no remedy in Python 2.x? Commented Nov 30, 2014 at 15:38

2 Answers 2

2

Qualified names for classes were introduced in Python 3.3 (see PEP 3155) and AFAIK there's no non-hacky way to do this on Python 2.

Sign up to request clarification or add additional context in comments.

Comments

2

In order to allow me to get the name of a class I introduced the following functions to introduce the PEP 3155 attribute __qualname__:

def fixup_module_class_names(modname=__name__):
    """
    Fixes the class types of the given module to have an attribute __qualname__
    as stipulated by PEP 3155.
    """
    def fixup_classtype_members(obj, prefix=None):
        """
        Fixes the __qualname__ attribute of the object as well as its contained
        member classes recursively.
        """
        from inspect import getmembers, isclass
        for n, t in getmembers(obj, isclass):
            if (t.__module__ != modname) or n == "__class__":
                continue
            setattr(t, "__qualname__", n if prefix is None else "%s.%s" % (prefix, n))
            fixup_classtype_members(t, getattr(t, "__qualname__"))
    fixup_classtype_members(sys.modules[modname])

Just call it as fixup_module_class_names() or by passing the module name whose class type members you want to have with __qualname___ attributes.

In order to account for the missing __qualname__ attributes on other class types I am then using typename(x) instead of type(x).__name__ where typename is defined as:

def typename(clsobj):
    "Retrieve the qualified name from an object or the name from anything else."
    return getattr(type(clsobj), "__qualname__", None) or getattr(type(clsobj), "__name__")

Note: If you, distinguished reader, find anything wrong with this approach for Python 2.x, please leave a comment or simply edit and fix the code as appropriate.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.