2011-07-15 17 views
8

Czy można uzyskać nazwę klasy w treści definicji klasy?Python nazwa klasy w klasie body

Na przykład

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

wiem, że mogę to zrobić statycznie zewnątrz ciała za pomocą metody klasy Klasa:

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

Bar.magic() 

jednak nie jest to, czego chcę, chcę, nazwa klasy w klasie body

+2

Nie można tego zrobić w Pythonie 2, ale metadlasy '__prepare__' w Pythonie 3 pozwala dodać nazwę do słownika klasy zanim rozpocznie się wykonywanie klasy klasy (skąd można z niej skorzystać) –

+0

Czy możesz opisać, w jaki sposób chcesz go użyć? Wydaje się to trochę bezsensowne, ponieważ możesz napisać Foo .__ name__ zamiast magic(). W pewnym kontekście może istnieć lepsze rozwiązanie. – viraptor

+0

@Rosh Oxymoron, myślę, że jest to możliwe, może nie być eleganckie lub wydajne lub co chcesz, ale tak długo, jak można modyfikować AST w czasie wykonywania, możesz zrobić, co chcesz ... – GaretJax

Odpowiedz

6

Ok - ma jeszcze jedno rozwiązanie - to nie jest tak skomplikowane!

import traceback 

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

class Something(object): 
    print magic() 

Zostanie wydrukowane "Coś". Nie jestem pewien, czy wyodrębniony format stosu jest w jakikolwiek sposób standaryzowany, ale działa dla Pythona 2.6 (i 2.7 i 3.1)

+0

Aargh, masz mnie (a nawet wątki bezpieczne)! : D – GaretJax

+0

@GaretJax Preferuję dynamiczny, sprawdzanie stosu, parsowanie metaklasu :) przynajmniej jest zły i nikt nie spróbuje go użyć. Mój wygląda niebezpiecznie blisko "użytecznego". – viraptor

+0

Mogę dodać to jako zaletę do mojego rozwiązania ... przynajmniej jest jeden. – GaretJax

4

AFAIK, obiekt klasy nie jest dostępny, dopóki definicja klasy nie zostanie "wykonana", więc nie można jej uzyskać podczas definicji klasy.

Jeśli potrzebujesz nazwy klasy do późniejszego użycia, ale nie używaj jej podczas definiowania klasy (na przykład do obliczania innych nazw pól lub czegoś podobnego), możesz nadal zautomatyzować proces za pomocą dekoratora klasy.

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

(Zastrzeżenie:. Nie testowane)

Z tej definicji, można uzyskać coś takiego:

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

i co można uzyskać pole x z nazwą klasy w nim, jak w:

print Foo.x 
+0

Hmm, dziękuję za to, że nie jest to dokładnie to, czego chciałem, ponieważ zawsze mogę uzyskać nazwę klasy później z __name__, ale wiedza o dekoratorach klas mogłaby prawdopodobnie rozwiązać mój problem. – drewrobb

+3

@drewrobb: być może możesz zadać pytanie dotyczące samego projektu, aby uzyskać alternatywne rozwiązania. –

-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' 

W ten sposób można użyć atrybutu x, przynajmniej w każdej instancji i metod statycznych. W metodach klasowych możesz mimo wszystko uzyskać nazwę klasy z atrybutu cls.

+0

To nie jest odpowiedź na moje pytanie, chcę uzyskać nazwę klasy z definicji klasy – drewrobb

+1

@drewrobb: I co z tym zrobić? Każdy wykonywany kod musiałby zostać wywołany z metody klasy, w którym to przypadku można uzyskać nazwę klasy z "cls .__ name__" lub "self .__ class __.__ name__", w zależności od rodzaju metody. Możesz także zrobić metaclass (http://docs.python.org/reference/datamodel.html#customizing-class-creation), jeśli próbujesz zrobić coś naprawdę dziwnego. – JAB

+0

Metaclasses w Pythonie 2 wciąż kopią po wykonaniu ciała, więc ciało klasy nie będzie znało nazwy klasy. –

1

Nie znam eleganckiego sposobu na zrobienie tego w Pythonie 2.x - ale jest to język interpretowany, co oznacza, że ​​coś stosunkowo prostego w następnych wierszach zrobi to, co chcesz i będzie bezpieczne, jeśli " re pewien kod wykonywany:

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

foo = Foo() 
print foo 
1

Tutaj masz rozwiązanie pracy dla konkretnego przypadku, ale uwaga (napisałem go głównie w celu wykazania, że ​​jest to rzeczywiście możliwe, aby zrobić coś takiego):

  • Nie należy jej używać
  • Jest to bardzo specyficzny
  • Ma wiele ograniczeń
  • właśnie zabawę z tym
  • Jest to czarna magia
  • To nie może działać w Twoim przypadku użycia
  • Nie jest wątkowo
  • Czy już powiedziałem, że nie powinieneś go używać?

Zresztą tu masz kod:

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__ 

Edit:https://gist.github.com/1085475 - nie masz wersję, która pozwala na wykorzystanie __clsname__ podczas wykonywania metody; nie ma większego sensu, ponieważ self.__class__.__name__ jest lepszym podejściem, a zmienna __clsname__ nie trzyma już napisu (eksperymentuję z tym)

+0

Co więcej, nie jest to bezpieczne dla wątków;) – viraptor

+0

Dzięki @viraport, dodałem ...;) Zastanawiam się, czy można to przezwyciężyć ... może poprzez zamknięcie wewnątrz funkcji 'NameAwareClassType' i odblokowanie w metaclassie Metoda "__new__" ... jeszcze więcej zła, blokowanie wątków do tworzenia ** klas **: D – GaretJax

Powiązane problemy