在Python中获取对象的完整限定类名

197
为了记录日志,我想获取Python对象的完全限定类名。(所谓完全限定指包括类名、包名及模块名。)
我知道 x.__class__.__name__,但是否有一种简单的方法来获取包和模块?
13个回答

213

使用以下程序

#!/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))

并且Bar被定义为

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

输出结果为

$ ./prog.py
foo.Bar
如果你仍然使用Python 2,那么你需要使用__name__而不是__qualname__,尤其是对于嵌套类来说,__name__的提示信息较少 - 在一个嵌套在Foo类中的Bar类将显示为Bar而不是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__

4
似乎返回的是定义bar的模块,而不是定义Bar的模块。如果记录日志的目的是要知道对象的确切类型,那么这似乎没有什么帮助。 - Mark E. Haase
o.__class__.__module__ 是否与 o.__module__ 有可能不同? - larsks
3
larsks: 是的,https://dev59.com/JVXTa4cB1Zd3GeqPyh5u - Yaroslav Bulatov
4
我强烈推荐在Python3中使用".".join([o.__module__, o.__name__]) - Piotr Pęczek
有时候不起作用:AttributeError: 'AttributeError' object has no attribute '__module__' - Hendy Irawan

57

提供的答案没有处理嵌套类。

自Python 3.3(PEP 3155)以来,您可以使用类的__qualname__而不是__name__。否则,像这样的一个类

class Foo:
    class Bar: # this one
        pass

将只显示为Bar,而不是Foo.Bar

(您仍需要单独将__module__附加到qualname中-__qualname__不包括模块名称。)


3
如果你使用https://github.com/wbolster/qualname,你可以在旧版本上获得一个等效于__qualname__的变量。 - wouter bolsterlee
14
即:Type.__module__ + '.' + Type.__qualname__。这段代码是将一个类型的模块名和限定名称连接起来形成一个完整的类型名。 - Kentzo
4
在Python 3.6中,这只会给我类名,不包括模块名。 - jpmc26
4
在Python 3.7中,__qualname__仍然只解析为类名。 - zepp133

30

这是基于Greg Bacon出色答案的改进版,但增加了一些额外的检查:

__module__ 可以为 None(根据文档),并且对于像 str 这样的类型,它可以是 __builtin__(您可能不希望在日志或其他地方出现)。以下代码检查了这两种可能性:

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__

(可能有更好的方法来检查 __builtin__,上述代码只是依赖于 str 始终可用,且其模块始终为 __builtin__


6
为了让其他人不必比较两个答案:Greg Bacon的答案中的代码现在与这个答案中的代码完全相同,除了Greg添加了一个无意义的else语句和一些代码注释。MB是真正的英雄。 - MarredCheese
str.__class__.__module__ 的评估结果为 'builtins',所以 if module is None or module == 'builtins' 有什么问题吗? - ChrisCrossCrash
文档中提到,对于函数而言,__module__ 可以是 None。但是对于类而言,如果 Python 在创建类型时无法确定模块,则 __module__ 属性根本就不存在 - 如果您尝试访问它,将会引发 AttributeError 异常。__module__ 也可以手动设置为任意对象。 - user2357112
检查 module is None 并不会增加任何有意义的安全性。处理 AttributeError 并检查类型可能更有用,但仍然不太可能有影响。 - user2357112
此外,如果您对__module__非常担心,那么您也必须对__class__同样持谨慎态度。实际上,__class__并不能保证是一个对象的类。例如,如果一个类具有不特殊处理__class____getattribute__覆盖,则它甚至可能根本不是一个类。 - user2357112

27

我使用的是Python3.7:

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

获取:

package.subpackage.ClassName

1
请注意,obj 必须是一个类而不是对象实例。 - Ivan De Paz Centeno

21

考虑使用inspect模块,该模块具有像getmodule这样的函数,可能是您正在寻找的内容:

>>>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'>

14
inspect.getmodule() 返回的是模块对象,而不是完全限定的类名。实际上,inspect 模块并没有提供任何功能来真正解决这个问题。这个答案是一个无关紧要的答案。</facepalm> - Cecil Curry
4
应该是 <facepalm/> - Shadi
除了在“builtins”模块中定义的类之外,一个类的完全限定名称通常表示该类的模块名称后缀为“cls.__name__”,现在我们有一个函数来确定类是否在“builtins”模块中定义,例如由inspect.getmodule(object)返回的模块。访问“module”类本身可能会更具挑战性,除非通过类似的间接方式? - Sean Champ

12

有些人(例如https://dev59.com/IHI-5IYBdhLWcg3wFkSO#16763814)认为__qualname____name__更好。

下面是一个示例,展示了它们之间的差异:

$ 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

请注意,它也可以正确地处理内置函数:

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

10
__module__ 可以解决这个问题。 尝试:
>>> import re
>>> print re.compile.__module__
re

这个网站建议使用__package__来适用于Python 3.0;但是,那里给出的例子无法在我的Python 2.5.2控制台中运行。


9
行了,谢谢! 对于完全限定名称,我会使用以下代码:"%s.%s" % (x.__class__.__module__, x.__class__.__name__) - Hanno S.

7
这只是一个hack,但我需要支持2.6版本,所以只需要一些简单的东西:
>>> from logging.handlers import MemoryHandler as MH
>>> str(MH).split("'")[1]

'logging.handlers.MemoryHandler'

2
这取决于在被检查的类中实现的__repr __()未被覆盖的__str __())。 在大多数情况下没有用。 - z33k

4
以下是对Greg Bacon所提供的答案进行改进后的结果,已针对类、实例、方法、函数以及内置和用户定义的情况进行了测试。
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

我们如何获取(全局)变量和属性的完整名称? - Piranna

3

由于该主题的兴趣在于获取完全限定名称,因此在使用相对导入时,如果与同一包中的主模块同时存在,则会出现一个陷阱。例如,在以下模块设置中:

$ 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

这里是输出的结果,展示了以不同方式导入相同模块的结果:
$ 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

当使用相对路径导入bar时,bar将Baz.__module__视为"baz",但在使用完整名称的第二个导入中,bar将其视为"foo.baz"。
如果您在某处持久化了全限定名称,最好避免对这些类使用相对导入。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接