214

For logging purposes I want to retrieve the fully qualified class name of a Python object. (With fully qualified I mean the class name including the package and module name.)

I know about x.__class__.__name__, but is there a simple method to get the package and module?

1
  • str(MyClass)[8:-2] Commented Sep 1, 2024 at 12:21

14 Answers 14

233

With the following program

#!/usr/bin/env python

import foo

def fullname(o):
    klass = o.__class__
    module = klass.__module__
    if module == 'builtins':
        return klass.__qualname__ # avoid outputs like 'builtins.str'
    return module + '.' + klass.__qualname__

bar = foo.Bar()
print(fullname(bar))

and Bar defined as

class Bar(object):
  def __init__(self, v=42):
    self.val = v

the output is

$ ./prog.py
foo.Bar

If you're still stuck on Python 2, you'll have to use __name__ instead of __qualname__, which is less informative for nested classes - a class Bar nested in a class Foo will show up as Bar instead of Foo.Bar:

def fullname(o):
    klass = o.__class__
    module = klass.__module__
    if module == '__builtin__':
        return klass.__name__ # avoid outputs like '__builtin__.str'
    return module + '.' + klass.__name__
Sign up to request clarification or add additional context in comments.

5 Comments

This appears to return the module where bar was defined, not where Bar was defined. If the purpose of logging is to know exactly what kind of object it was, then this doesn't seem to help.
Is o.__class__.__module__ ever different from o.__module__?
I strongly recomend ".".join([o.__module__, o.__name__]) for Python3
Doesn't work sometimes: AttributeError: 'AttributeError' object has no attribute '__module__'
67

The provided answers don't deal with nested classes.

Since Python 3.3 (PEP 3155), you can use __qualname__ of the class instead of the __name__. Otherwise, a class like

class Foo:
    class Bar: # this one
        pass

will show up as just Bar instead of Foo.Bar.

(You'll still need to attach the __module__ to the qualname separately - __qualname__ is not intended to include module names.)

5 Comments

If you use github.com/wbolster/qualname you can get a qualname equivalent on older versions.
I.e. Type.__module__ + '.' + Type.__qualname__.
With Python 3.6, this only gives me the class name. It doesn't include the module.
@jpmc26 In Python 3.7, __qualname__ still only resolves to the class name
so what is the fix
35

Here's one based on Greg Bacon's excellent answer, but with a couple of extra checks:

__module__ can be None (according to the docs), and also for a type like str it can be __builtin__ (which you might not want appearing in logs or whatever). The following checks for both those possibilities:

def fullname(o):
    module = o.__class__.__module__
    if module is None or module == str.__class__.__module__:
        return o.__class__.__name__
    return module + '.' + o.__class__.__name__

(There might be a better way to check for __builtin__. The above just relies on the fact that str is always available, and its module is always __builtin__)

5 Comments

To save others the effort of comparing the two answers: the code in Greg Bacon's answer is now identical to the code in this one, other than that Greg added a pointless else statement and some code comments. MB is the real hero.
str.__class__.__module__ evaluates to 'builtins', so would there be anything wrong with if module is None or module == 'builtins'?
The docs say __module__ can be None for functions, but for classes, if Python can't determine a module when creating a type, the __module__ attribute just won't exist at all - you'll get an AttributeError if you try to access it. __module__ can also be manually set to arbitrary objects.
Checking module is None thus doesn't add any meaningful safety. Handling AttributeError and checking the type would be more useful, though still unlikely to matter.
Also, if you're that paranoid about __module__, you have to be equally paranoid about __class__, which is not actually guaranteed to be an object's class. It might not even be a class at all, for example if a class has a __getattribute__ override that doesn't special-case __class__.
32

For python3.7 I use:

".".join([obj.__module__, obj.__name__])

Getting:

package.subpackage.ClassName

1 Comment

note that obj must be a class rather than an object instance.
22

Consider using the inspect module which has functions like getmodule which might be what are looking for:

>>>import inspect
>>>import xml.etree.ElementTree
>>>et = xml.etree.ElementTree.ElementTree()
>>>inspect.getmodule(et)
<module 'xml.etree.ElementTree' from 
        'D:\tools\python2.5.2\lib\xml\etree\ElementTree.pyc'>

3 Comments

inspect.getmodule() returns module objects – not fully qualified class names. In fact, the inspect module provides no functionality whatsoever that would actually address this question. This answer is a non-answer. </facepalm>
Should be <facepalm/>
Helpful. Except for classes defined in the builtins module, the fully qualified name of a class might typically represent the class' module name suffixed with the cls.__name__, and now we have a function for determining if the class is defined in the builtins module, e.g that module returned by inspect.getmodule(object). Accessing the module class in itself might seem to be a bit more challenging, except by a similar indirection?
13

Some people (e.g. https://stackoverflow.com/a/16763814/5766934) arguing that __qualname__ is better than __name__. Here is an example that shows the difference:

$ cat dummy.py 
class One:
    class Two:
        pass

$ python3.6
>>> import dummy
>>> print(dummy.One)
<class 'dummy.One'>
>>> print(dummy.One.Two)
<class 'dummy.One.Two'>
>>> def full_name_with_name(klass):
...     return f'{klass.__module__}.{klass.__name__}'
>>> def full_name_with_qualname(klass):
...     return f'{klass.__module__}.{klass.__qualname__}'
>>> print(full_name_with_name(dummy.One))  # Correct
dummy.One
>>> print(full_name_with_name(dummy.One.Two))  # Wrong
dummy.Two
>>> print(full_name_with_qualname(dummy.One))  # Correct
dummy.One
>>> print(full_name_with_qualname(dummy.One.Two))  # Correct
dummy.One.Two

Note, it also works correctly for builtins:

>>> print(full_name_with_qualname(print))
builtins.print
>>> import builtins
>>> builtins.print
<built-in function print>

Comments

11

__module__ would do the trick.

Try:

>>> import re
>>> print re.compile.__module__
re

This site suggests that __package__ might work for Python 3.0; However, the examples given there won't work under my Python 2.5.2 console.

1 Comment

That does the trick, thanks! For the fully qualified name I will use "%s.%s" % (x.__class__.__module__, x.__class__.__name__)
7

This is a hack but I'm supporting 2.6 and just need something simple:

>>> from logging.handlers import MemoryHandler as MH
>>> str(MH).split("'")[1]

'logging.handlers.MemoryHandler'

1 Comment

This depends on the __repr__() implementation in the checked class (and on __str__() not being overriden). Useless in most cases.
5

Bellow is just an improvement of Greg Bacon's answer, tested for class, instance, method, function, both builtin and user defined.

def fullname(o):
    try:
        # if o is a class or function, get module directly
        module = o.__module__
    except AttributeError:
        # then get module from o's class
        module = o.__class__.__module__
    try:
        # if o is a class or function, get name directly
        name = o.__qualname__
    except AttributeError:
        # then get o's class name
        name = o.__class__.__qualname__
    # if o is a method of builtin class, then module will be None
    if module == 'builtins' or module is None:
        return name
    return module + '.' + name

2 Comments

How can we get the full name of (global) variables and Properties?
@Piranna class and def record their name and module but for variables that's not generally possible. E.g. myvar = None, it's not even a fresh instance, there is no MAGIC(myvar) that could look at the None object and trace it back to specific module/variable you had in mind... Kludge: for unique values, gc.get_referrers() may find the module, and then you can iterate module's .__dict__ to find the name [it's slow; see more caveats in its doc]. But if you got the value without knowing wherefrom, you'll have >1 referers.
3

I'm using Python 3.12 because this is 2024. If you're still using Python 2, whoever is your boss, they deserve the pain. For those living in the XXI century, this has been doing it for me:

who = lambda obj: f'{obj.__class__.__module__}.{obj.__class__.__qualname__}'

No need to call '.'.join() (much less make a list for it when a tuple does the same and is CHEAPER!) and there's this f-string thing, which is better than the % stuff by a landslide.

I've been using this in my own objects, so no edge cases where __module__ might be None, but I tried it against all sorts of things — including None! — and this simply works.

Comments

2

Since the interest of this topic is to get fully qualified names, here is a pitfall that occurs when using relative imports along with the main module existing in the same package. E.g., with the below module setup:

$ cat /tmp/fqname/foo/__init__.py
$ cat /tmp/fqname/foo/bar.py
from baz import Baz
print Baz.__module__
$ cat /tmp/fqname/foo/baz.py
class Baz: pass
$ cat /tmp/fqname/main.py
import foo.bar
from foo.baz import Baz
print Baz.__module__
$ cat /tmp/fqname/foo/hum.py
import bar
import foo.bar

Here is the output showing the result of importing the same module differently:

$ export PYTHONPATH=/tmp/fqname
$ python /tmp/fqname/main.py
foo.baz
foo.baz
$ python /tmp/fqname/foo/bar.py
baz
$ python /tmp/fqname/foo/hum.py
baz
foo.baz

When hum imports bar using relative path, bar sees Baz.__module__ as just "baz", but in the second import that uses full name, bar sees the same as "foo.baz".

If you are persisting the fully-qualified names somewhere, it is better to avoid relative imports for those classes.

2 Comments

The problem with this comment is it's getting confused due to the PYTHONPATH adjustments performed by direct script execution: when executing a .py file, Python will automatically add that file's directory on the path. So python /tmp/fqname/foo/bar.py has a pythonpath of /tmp/fqname/foo:/tmp/fqname, and the "import baz" resolves to an absolute import not a relative one.
Trying this out in Python 3 will fail, because implicit relative imports have stopped being a thing, so bar.py and hum.py either use from .baz and the direct execution fails, or they use import baz and main.py fails. For it to work in both cases, they have to use an actual absolute import (from foo.baz import Baz), which disambiguates the import, and precludes an unexpected absolute import of baz as a top-level package.
2

This is an adaption of the answers by Greg Bacon and MB to use the qualified class name. Note that the question did ask for the qualified class name. It was tested with Python 3.8.

def fullname(obj: object) -> str:
    """Return the full name of the given object using its module and qualified class names."""
    # Ref: https://stackoverflow.com/a/66508248/
    module_name, class_name = obj.__class__.__module__, obj.__class__.__qualname__
    if module_name in (None, str.__class__.__module__):
        return class_name
    return module_name + "." + class_name

Comments

1

My solution is:

def fullname(obj) -> str:
    if type(obj).__qualname__ != "type":
        # obj is instance
        return ".".join(
            [
                obj.__class__.__module__,
                obj.__class__.__qualname__,
            ]
        )
    # obj is not instance
    return ".".join([obj.__module__, obj.__qualname__])
 
 # not instance
 >>> print(fullname(datetime))
 "datetime.datetime"
 # instance
 >>> print(fullname(datetime.now())
 "datetime.datetime"
 # instance
 >>> print(fullname(3))
 "builtins.int"
 

Comments

0

None of the answers here worked for me. In my case, I was using Python 2.7 and knew that I would only be working with newstyle object classes.

def get_qualified_python_name_from_class(model):
    c = model.__class__.__mro__[0]
    name = c.__module__ + "." + c.__name__
    return name

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.