รับชื่อคลาสแบบเต็มของอ็อบเจ็กต์ใน Python


138

สำหรับวัตถุประสงค์ในการบันทึกฉันต้องการดึงชื่อคลาสแบบเต็มของอ็อบเจ็กต์ Python (ด้วยคุณสมบัติครบถ้วนฉันหมายถึงชื่อคลาสรวมถึงแพ็คเกจและชื่อโมดูล)

ฉันรู้x.__class__.__name__แต่มีวิธีง่ายๆในการรับแพ็คเกจและโมดูลหรือไม่?

คำตอบ:


150

ด้วยโปรแกรมดังต่อไปนี้

#! /usr/bin/env python

import foo

def fullname(o):
  # o.__module__ + "." + o.__class__.__qualname__ is an example in
  # this context of H.L. Mencken's "neat, plausible, and wrong."
  # Python makes no guarantees as to whether the __module__ special
  # attribute is defined, so we take a more circumspect approach.
  # Alas, the module name is explicitly excluded from __qualname__
  # in Python 3.

  module = o.__class__.__module__
  if module is None or module == str.__class__.__module__:
    return o.__class__.__name__  # Avoid reporting __builtin__
  else:
    return module + '.' + o.__class__.__name__

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

และBarกำหนดเป็น

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

ผลลัพธ์คือ

$ ./prog.py
foo.Bar

2
สิ่งนี้ดูเหมือนจะส่งคืนโมดูลที่กำหนดแถบไม่ใช่ที่กำหนด Bar หากจุดประสงค์ของการบันทึกคือเพื่อให้ทราบแน่ชัดว่าเป็นวัตถุชนิดใดสิ่งนี้ดูเหมือนจะไม่ช่วยอะไร
Mark E. Haase

คือo.__class__.__module__เคยแตกต่างจากo.__module__?
larsks


1
ฉันขอแนะนำอย่างยิ่ง".".join([o.__module__, o.__name__])สำหรับ Python3
Piotr Pęczek

ใช้งานไม่ได้ในบางครั้ง:AttributeError: 'AttributeError' object has no attribute '__module__'
Hendy Irawan

39

คำตอบที่ให้มาไม่เกี่ยวข้องกับชั้นเรียนที่ซ้อนกัน แม้ว่าจะไม่สามารถใช้งานได้จนถึง Python 3.3 ( PEP 3155 ) แต่คุณก็ต้องการใช้__qualname__คลาสนี้จริงๆ ในที่สุด (3.4? PEP 395 ) __qualname__จะมีอยู่สำหรับโมดูลเพื่อจัดการกับกรณีที่โมดูลถูกเปลี่ยนชื่อ (เช่นเมื่อเปลี่ยนชื่อเป็น__main__)


3
หากคุณใช้github.com/wbolster/qualnameคุณจะได้รับqualnameเทียบเท่ากับเวอร์ชันเก่า
wouter bolsterlee

3
Type.__module__ + '.' + Type.__qualname__กล่าวคือ
Kentzo

4
ด้วย Python 3.6 สิ่งนี้ทำให้ฉันมีชื่อคลาสเท่านั้น ไม่รวมโมดูล
jpmc26

3
@ jpmc26 ใน Python 3.7 __qualname__ยังคงแก้ไขเฉพาะชื่อคลาส
zepp.lee

22

พิจารณาใช้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'>

9
inspect.getmodule()ส่งคืนอ็อบเจ็กต์โมดูล - ชื่อคลาสไม่ถูกต้อง ในความเป็นจริงinspectโมดูลไม่มีฟังก์ชันใด ๆ ที่จะตอบคำถามนี้ได้จริง คำตอบนี้เป็นคำตอบที่ไม่ใช่ </facepalm>
Cecil Curry

19

นี่คือคำตอบที่ดีเยี่ยมของ 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__)


4
เพื่อช่วยให้ผู้อื่นไม่ต้องพยายามเปรียบเทียบคำตอบทั้งสอง: ตอนนี้รหัสในคำตอบของ Greg Bacon เหมือนกับรหัสในข้อนี้นอกเหนือจากนั้น Greg ได้เพิ่มคำสั่งอื่นที่ไม่มีจุดหมายและความคิดเห็นเกี่ยวกับรหัส MB คือฮีโร่ตัวจริง
MarredCheese


9

__module__ จะทำเคล็ดลับ

ลอง:

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

ไซต์นี้แนะนำว่า__package__อาจใช้ได้กับ Python 3.0 อย่างไรก็ตามตัวอย่างที่ให้มาจะไม่ทำงานภายใต้คอนโซล Python 2.5.2 ของฉัน


6
นั่นเป็นเคล็ดลับขอบคุณ! สำหรับชื่อที่มีคุณสมบัติครบถ้วนฉันจะใช้ "%s.%s" % (x.__class__.__module__, x.__class__.__name__)
Hanno S.

7

นี่เป็นการแฮ็ก แต่ฉันรองรับ 2.6 และต้องการอะไรง่ายๆ:

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

'logging.handlers.MemoryHandler'

1
นี้ขึ้นอยู่กับ__repr__()การดำเนินการในชั้นการตรวจสอบ ( และใน__str__()การที่จะไม่ overriden) ไม่มีประโยชน์ในกรณีส่วนใหญ่
z33k

7

บางคน (เช่น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

หมายเหตุมันยังทำงานได้อย่างถูกต้องสำหรับ buildins:

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

2

เนื่องจากความสนใจของหัวข้อนี้คือการได้รับชื่อที่มีคุณสมบัติครบถ้วนนี่คือข้อผิดพลาดที่เกิดขึ้นเมื่อใช้การนำเข้าแบบสัมพัทธ์ร่วมกับโมดูลหลักที่มีอยู่ในแพ็คเกจเดียวกัน เช่นด้วยการตั้งค่าโมดูลด้านล่าง:

$ 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 โดยใช้เส้นทางสัมพัทธ์แถบจะเห็นBaz.__module__เป็นเพียง "baz" แต่ในการนำเข้าครั้งที่สองที่ใช้ชื่อเต็ม bar จะเห็นเหมือนกับ "foo.baz"

หากคุณยังคงใช้ชื่อที่มีคุณสมบัติครบถ้วนอยู่ที่ไหนสักแห่งจะเป็นการดีกว่าที่จะหลีกเลี่ยงการนำเข้าแบบสัมพัทธ์สำหรับคลาสเหล่านั้น


0

ไม่มีคำตอบใดที่ได้ผลสำหรับฉัน ในกรณีของฉันฉันใช้ Python 2.7 และรู้ว่าฉันจะทำงานกับobjectคลาสสไตล์ใหม่เท่านั้น

def get_qualified_python_name_from_class(model):
    c = model.__class__.__mro__[0]
    name = c.__module__ + "." + c.__name__
    return name
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.