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

出于日志记录的目的,我想检索 Python 对象的完全限定类名。(完全限定是指包括包和模块名的类名。)

我知道 x.__class__.__name__,但是有没有一个简单的方法来获取包和模块?

122051 次浏览

__module__就够了。

试试:

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

这个站点 表明 __package__可能适用于 Python 3.0; 但是,那里给出的示例在我的 Python 2.5.2控制台下无法工作。

考虑使用 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'>

下面的节目

#!/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__,这对于嵌套类来说信息量较小——嵌套在类 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__

由于本主题的目的是获得完全限定名称,因此在使用相对导入和同一包中存在的主模块时会出现一个缺陷。例如,使用下面的模块设置:

$ 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

当 hum 使用相对路径导入 bar 时,bar 将 Baz.__module__视为“ baz”,但在使用全名的第二次导入中,bar 将看到与“ foo.baz”相同的内容。

如果将完全限定名保存在某个地方,最好避免这些类的相对导入。

以下是基于格雷格 · 培根(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__)

所提供的答案不处理嵌套类。

由于 Python 3.3(PEP 3155) ,您可以使用类的 __qualname__而不是 __name__

class Foo:
class Bar: # this one
pass

将显示为 Bar而不是 Foo.Bar

(您仍然需要将 __module__分别附加到 qualname-__qualname__不打算包含模块名称。)

这是一个黑客,但我支持2.6,只是需要一些简单的东西:

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


'logging.handlers.MemoryHandler'

这里所有的答案对我都不起作用。在我的例子中,我使用的是 Python 2.7,并且知道我只能使用新风格的 object类。

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

有些人(例如 https://stackoverflow.com/a/16763814/5766934)认为 __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>

对于 python3.7,我使用:

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

获得:

package.subpackage.ClassName

这是对 Greg BaconMB答案的改编,以使用限定类名。请注意,该问题确实要求使用限定类名。它使用 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


Bellow 只是 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

我的解决办法是:

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"