【问题标题】:Python name of class in class body类主体中类的 Python 名称
【发布时间】:2011-07-15 17:27:32
【问题描述】:

是否可以在类定义的主体中获取类名?

例如,

class Foo():
    x = magic() # x should now be 'Foo'

我知道我可以使用类方法在类主体之外静态地执行此操作:

class Bar():
    @classmethod
    def magic(cls):
        print cls.__name__

Bar.magic()

但这不是我想要的,我想要类体中的类名

【问题讨论】:

  • 这在 Python 2 中是不可能的,但是 Python 3 中元类的 __prepare__ 方法允许您在类的主体开始执行之前将名称添加到类字典中(从您然后可以使用它)
  • 您能描述一下您想如何使用它吗?这本身似乎有点毫无意义,因为您可以编写 Foo.__name__ 而不是 magic()。在某些情况下,可能会有更好的解决方案。
  • @Rosh Oxymoron,我认为这是可能的,它可能不是优雅或高性能或你想要的,但只要你可以在运行时修改 AST,你可以做任何你想做的事情。 .
  • @Rosh Oxymoron:请参阅下面我提出的解决方案;)
  • @phkahler:我有很多子类,我想静态地(在导入时)执行一些初始化方法,以从需要子类名称的超类中设置一些静态变量。我不希望将子类名称作为这些方法的输入的代码重复。我需要在拥有类实例之前完成此设置。另外,我不想使用@classmethod 进行初始化,因为我必须在某个地方调用类方法。

标签: python metaprogramming python-2.x


【解决方案1】:

好的 - 还有一个解决方案 - 这个其实并不复杂!

import traceback

def magic():
   return traceback.extract_stack()[-2][2]

class Something(object):
   print magic()

它会打印出“Something”。我不确定提取的堆栈格式是否以任何方式标准化,但它适用于 python 2.6(以及 2.7 和 3.1)

【讨论】:

  • @GaretJax 我更喜欢动态的、堆栈检查的、文本解析的元类 :) 至少它是邪恶的,没有人会尝试使用它。我的看起来非常接近“可用”。
  • 我可以将此作为优势添加到我的解决方案中......至少有一个。
  • 谢谢!这是非常好的。
【解决方案2】:

AFAIK,在“执行”类定义之前,类对象不可用,因此在类定义期间无法获取它。

如果您需要类名以供以后使用,但在类定义期间不使用它(例如,计算其他字段名或类似的事情),那么您仍然可以使用类装饰器自动化该过程。

def classname ( field ):
    def decorator ( klass ):
        setattr(klass, field, klass.__name__)
        return klass
    return decorator

(警告:未测试。)

有了这个定义,你可以得到类似的东西:

@classname(field='x')
class Foo:
    pass

你会得到带有类名的字段x,如下所示:

print Foo.x

【讨论】:

  • 嗯,谢谢你,这不是我想要的,因为我以后总是可以用 name 获取类名,但是了解类装饰器也许可以解决我的问题。
  • @drewrobb:也许您可以询问有关设计本身的问题以获得替代解决方案。
【解决方案3】:

我不知道在 Python 2.x 中有一种优雅的方式来做到这一点——但它是一种解释性语言,这意味着按照以下几行相对简单的东西会做你想做的事情,如果你是确定正在执行的代码:

classdef = """\
class %(classname)s(object):
    x = '%(classname)s'
    print x
"""
exec classdef % {'classname': 'Foo'}

foo = Foo()
print foo

【讨论】:

    【解决方案4】:

    在这里,您有一个适用于您的特定案例的有效解决方案,但请注意(我写它主要是为了证明确实可以做这样的事情):

    • 你不应该使用它
    • 很具体
    • 它有很多限制
    • 我只是在玩这个
    • 这是黑魔法
    • 它可能不适用于您的用例
    • 它不是线程安全的
    • 我是否已经说过你不应该使用它?

    不管怎样,这里有代码:

    import inspect
    
    def NameAwareClassType():
        frameInfo = inspect.getouterframes(inspect.currentframe())[1]
        codeContext = frameInfo[4][0]
        className = codeContext.split(' ', 1)[1].split('(', 1)[0]
    
        class ClassNameGlobalRemoverType(type):
            def __new__(mcs, name, bases, dict):
                if name == className:
                    del globals()['__clsname__']
                return type.__new__(mcs, name, bases, dict)
    
        class NameAwareClass(object):
            __metaclass__ = ClassNameGlobalRemoverType
    
        globals()['__clsname__'] = className
    
        return NameAwareClass
    
    class A(NameAwareClassType()):
    
        print __clsname__
    
        def __init__(self):
            pass
    
    print __clsname__
    

    编辑: https://gist.github.com/1085475 — 你有一个版本允许在方法执行期间使用 __clsname__;没有多大意义,因为self.__class__.__name__ 是一种更好的方法,而__clsname__ 变量不再包含字符串(我很高兴尝试这个)

    【讨论】:

    • 谢谢@viraport,添加了它......方法...更邪恶,锁定线程来创建:D
    【解决方案5】:
    class Bar():
    
        @classmethod
        def magic(cls):
            return cls.__name__
    
        @property
        def x(self):
            return self.magic()
    
        def y(self):
            return self.x
    
    >>> a = Bar()
    >>> a.x
    'Bar'
    >>> a.y()
    'Bar'
    

    这样您就可以使用x 作为属性,至少在任何实例和静态方法中是这样。在类方法中,无论如何你都可以从cls 属性中获取类名。

    【讨论】:

    • 这不是我问题的真正答案,我想从类定义中获取类名
    • @drewrobb:用它做什么?任何正在执行的代码都必须从类的方法中调用,在这种情况下,您可以从cls.__name__self.__class__.__name__ 获取类名,具体取决于方法的类型。如果你想做一些非常奇怪的事情,你也可以make a metaclass for it
    • Python 2 中的元类在主体执行后仍会启动,因此类的主体不会知道类的名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-19
    • 2017-12-20
    • 1970-01-01
    • 2016-08-02
    • 1970-01-01
    相关资源
    最近更新 更多