Python获取类名

20

所以我有一个问题,我想以这种方式获取Python类的名称:

class TestClass():
    myName = (get class name here automatically)
    saveDirectory = os.path.join(saveDir, myName) # so i can save it in unique location
    def __init__(self):
        pass # do something

然而,在 myName 被创建时似乎尚不存在 __class__.__name__。因此,我不得不将其放入 __init__() 函数中,就像这样:

class TestClass():
    def __init__(self):
        self.myName = self.__class__.__name__
        saveDirectory = os.path.join(saveDir, self.myName) # so i can save it in unique location
        pass # do something

但是这有一个很大的问题,因为我在实例化类之前无法获取类的名称,我打算为每个类的实例创建几个千兆字节的数据,并将其保存在saveDirectory中,以便以后可以重复使用。所以我实际上不想继续使用我的当前解决方案。

有没有办法按照我的意图获取类名?还是我只是在做梦?

编辑:

感谢大家的建议。我将花点时间看看元类。否则,我可能会全局创建一个字典,并引用这些已实例化的类。


1
如果您想在定义类之后立即获取其名称,那么常量字符串属性是否足够?如果需要,您可以在子类中更改它。 - loopbackbee
3
如果你总是在使用类名,为什么不直接使用字符串?类名是静态的,所以没有理由不断地询问它。 - Serdalis
无法在类定义内部获取类型名称,因为在解释时,该类尚未创建。 - poke
你可以在元类或装饰器中实现它。但那太疯狂了。具体实现留给读者自行练习。 - Eevee
7
__init__()中把self.MyName设置为self.__class__.__name__是没有必要的。直接使用self.__class__.__name__即可! - kindall
7个回答

15

自 Python 3.3 版本起,你可以使用 __qualname__ 变量:

class TestClass():
    myName = __qualname__

8

只是使用一个方法吗?

class TestClass(object):
    @classmethod
    def get_save_directory(cls):
        return os.path.join(save_dir, cls.__name__)

或者你可以使用一个类属性来指示保存目录,减少魔法通常是一件好事。此外,您稍后还可以更改类的名称而不会破坏存储。

另外,什么?千兆字节?!


7

在类定义期间实际执行您要执行的操作的唯一方法是使用元类

def saveDirMeta(name, bases, dct):
    dct['saveDirectory'] = os.path.join(saveDir, name)
    return type(name, bases, dct)

class TestClass(object):
    __metaclass__ = saveDirMeta  # this adds the 'saveDirectory' attribute
    def __init__(self):
        pass # do something

或者在 Python 3.x 上:

class TestClass(metaclass=saveDirMeta):
    def __init__(self):
        pass # do something

以下方式会更加清晰明了:

class TestClass():
    saveDirectory = os.path.join(saveDir, 'TestClass')
    def __init__(self):
        pass # do something

2

我猜你想做的事情是不可能的,因为当你执行以下代码时,类甚至还不存在:

myName =(自动获取类名)

但是在类被解释后不久,你可以将类对象传递给其他函数来完成工作。

In [1]: def save_class(cl):
    myName = cl.__name__
    print myName        #or do whatever you wanna do here
   ...:     

In [2]: class A:pass

In [3]: save_class(A)
A

2

在课堂解释中,还没有“那里”存在。因此,是的,您最初的示例所显示的内容是不可能的。但请解释为什么需要在课程实际存在之前就知道这一点?

如果没有对这个未解决问题的好答案,那么只能尝试从以下方式中获取课程名称:

1. outside the interpreted class definition, uninstantiated

    class demo(object):
        pass

    print demo.__name__ # 'demo'


2. from within the class definition, uninstantiated (or instantiated ;-)

    class demo2(object):
        @classmethod
        def myName(cls):
            return cls.__name__

    print demo2.myName() # 'demo2'
    print demo2().myName() # 'demo2'


or

3. within an actual instance of the class

    class demo3(object):
        def myClassName(self):
            return self.__class__.__name__

    print demo3().myClassName() # 'demo3'

除此之外,请编辑问题并添加一些细节,解释为什么您的初始尝试需要按照您想要的方式工作,即在实例化之前但在类定义内部,而您的第二个示例存在问题。另外,saveDir是如何传递/来自哪里的?(在类之外的全局变量?)请展示一些代码,展示您打算如何使用您想要的内容。可能有一种重新设计的方法可以达到这个目标,即使不是字面上。


2

除了F J的答案中的元类外,我还可以想到另外两种方式。我将从外观更好的那个开始。您不需要使用元类;有时描述符是完全足够的。

class ClassNameDescriptor(object):
    def __get__(self, instance, owner):
        return owner.__name__

class BaseClass(object):
    name = ClassNameDescriptor()

class Foo(BaseClass):
    pass

如果你真的需要在类体内部获取类名(不太可能),另一种方法是通过调用堆栈来进行查找。 在类定义中执行的代码对象上,类名会按照源代码中的方式显示:

import sys

def get_class_name_from_inside_class():
    return sys._getframe().f_back.f_code.co_name

class Foo(object):
    name = get_class_name_from_inside_class()
    print name

到目前为止,只有最后一个选项允许您在类主体内使用名称作为字符串。不幸的是,这在所有版本的Python中都不起作用,特别是IronPython没有实现调用堆栈内省,因为它在DLR中不可用。
有一种方式可以在不内省堆栈的情况下轻松获得它。它类似于F J提供的答案,使用元类,但是改为使用Python 3中可用的__prepare__功能:
class namedMeta(type):
    def __prepare__(mcls, name, bases, **kwargs):
        return {'name': name}

class Foo(metaclass=named):
    print name

0

试试这个。你可以通过 _class 引用当前类。(我使用的是 Python 2.7)

class Meta(type):
    def __init__(self, *args):
        super(Meta, self).__init__(*args)
        self._class = self
        self.saveDir = str(self._class)

class Wow(object):
     __metaclass__ = Meta

>>> Wow.saveDir
>>> Wow().saveDir

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