2011-07-15 196 views
8

是否可以在班級定義的正文中獲取班級名稱?班級體內班級的Python名稱

例如,

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

我知道我可以使用一個類的方法做到這一點靜態外的類主體的:

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

Bar.magic() 

然而,這不是我想要的,我想在類體內

+2

這不可能在Python 2中完成,但Python 3中的元類的__prepare__方法允許您在類的主體開始執行之前將名稱添加到類字典中(您可以從那裏使用它) –

+0

您能描述一下您想如何使用它?這似乎有點無意義,因爲你可以寫Foo .__ name__而不是magic()。在某些情況下,可能會有更好的解決方案。 – viraptor

+0

@Rosh Oxymoron,我認爲這是可能的,它可能不是優雅或高性能或你想要的,但只要你可以在運行時修改AST,你可以做任何你想要的... – GaretJax

回答

6

好 - 救一個解決方案 - 這一塊其實沒有那麼複雜!

import traceback 

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

class Something(object): 
    print magic() 

它會打印出 「東西」。我不確定提取的堆棧格式是否以任何方式標準化,但它適用於Python 2.6(和2.7和3.1)

+0

Aargh,你讓我(甚至是線程安全)! :D – GaretJax

+0

@GaretJax我更喜歡動態,堆棧檢查,文本解析元類:)至少它是邪惡的,沒有人會嘗試使用它。我看起來危險地接近「可用」。 – viraptor

+0

我可以添加這個作爲我的解決方案的優勢...至少有一個。 – GaretJax

4

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 
+0

嗯,謝謝你,這不是我想要的,因爲我總是可以用__name__獲得類名,但瞭解類裝飾器可能可以解決我的問題。 – drewrobb

+3

@drewrobb:也許你可以問一個關於設計本身的問題來獲得替代解決方案。 –

-2
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屬性中獲取類名即可。

+0

這不是我的問題的答案,我想從類定義 – drewrobb

+1

@drewrobb中獲取類名:並且用它做什麼?任何正在執行的代碼都必須從類的方法中調用,在這種情況下,您可以根據方法的類型從「cls .__ name__」或「self .__ class __.__ name__」中獲取類名稱。如果你想做一些非常奇怪的事情,你也可以[爲它創建一個元類](http://docs.python.org/reference/datamodel.html#customizing-class-creation)。 – JAB

+0

Python 2中的元類仍然在執行主體後踢入,因此類的主體不知道類的名稱。 –

1

我不知道的一種優雅的方式在Python 2.x到做到這一點 - 但它是一種解釋型語言,這意味着什麼大意如下比較簡單會做你想要什麼,如果你」將是安全的重新確定的代碼被執行:

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

foo = Foo() 
print foo 
1

在這裏,你有你的特定情況下,有效的解決方案,但要小心(我寫它主要是爲了證明它確實有可能做這樣的事情):

  • 你不應該使用它
  • 這是非常具體的
  • 它有很大的侷限性
  • 我只是有這個
  • 樂趣,是黑魔法
  • 它可能不適合你的使用情況工作
  • 它不是線程安全的
  • 我是否已經說過你不應該使用它?

總之,在這裏你有代碼:

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__變量已經不抱一個字符串(我很開心這個實驗)

+0

此外,它不是線程安全的;) – viraptor

+0

感謝@viraport,增加了它;;)我想知道是否有可能克服這個......也許通過鎖定'NameAwareClassType'函數並在元類中解鎖'__new__'方法...更加邪惡,鎖定線程來創建**類**:D – GaretJax