2010-10-12 10 views
9

Staram się pisać rodzajowe metaklasa do śledzenia podklasyGeneric Python metaklasa śledzić podklasy

ponieważ chcę to mieć charakter ogólny, nie chciałam hardcode dowolną nazwę klasy w tym metaklasą, dlatego wpadłem funkcji, która generuje właściwą metaklasa, coś jak:

def make_subtracker(root): 
    class SubclassTracker(type): 
     def __init__(cls, name, bases, dct): 
      print('registering %s' % (name,)) 
      root._registry.append(cls) 
      super(SubclassTracker, cls).__init__(name, bases, dct) 
    return SubclassTracker 

ten sposób mogę powołać go do generowania metaklasa dla konkretnego korzeń klasie:

__metaclass__ = make_subtracker(Root) 

Oto, gdzie wpadam na problem. Nie mogę tego zrobić:

class Root(object): 
    _registry = [] 
    __metaclass__ = make_subtracker(Root) 

... bo Root nie została jeszcze określona, ​​gdy używam make_subtracker(Root). Próbowałem dodanie metaklasa atrybut później, tak, że przynajmniej można go stosować w podklasach:

class Root(object): 
    _registry = [] 

Root.__metaclass__ = make_subtracker(Root) 

... ale to nie działa. metaklasa ma specjalną obróbkę, gdy definicja klasy jest czytać, jak określono w http://docs.python.org/reference/datamodel.html#customizing-class-creation

szukam sugestii w tym celu (albo zmienić klasę metaklasa w czasie wykonywania w sposób, który jest stosowany do jego podklas lub jakąkolwiek inną alternatywę).

+1

Nie rób tego. Ludzie, którzy przyjdą po ciebie, rozwiążą to, ponieważ jest to zbyt skomplikowane. Użyj funkcji fabrycznej, która tworzy obiekty odpowiedniej podklasy. –

Odpowiedz

8

Python robi to automatycznie dla klas w nowym stylu, jak wspomniano w tym answer do analogicznego Queston How can I find all subclasses of a given class in Python? tutaj.

+1

Chociaż to technicznie śledzić, w zależności od użycia, może nie być bardzo wydajny. Zwraca listę, więc jeśli szukasz klasy o określonych atrybutach, będziesz musiał powtórzyć całą listę. Korzystanie z klasy meta pozwala na efektywne indeksowanie podszewek, jak tylko chcesz. – Cerin

+0

@Cerin: Być może ... jeśli wysoka wydajność jest problemem lub problemem - którego nie można określić na podstawie pytania, które nie określa sposobu wykorzystania informacji. Niezależnie od tego, byłbym zainteresowany widzeniem konkretnej implementacji twojego pomysłu, ponieważ wszystkie inne odpowiedzi są obecnie oparte na listach - możesz więc dodać własną. – martineau

+0

@martinaeu, Moja implementacja jest taka sama jak w aaronasterling, z tym wyjątkiem, że rejestr jest słownikiem zamiast listy, a kluczem jest dowolna mieszana reprezentacja, której chcesz użyć dla każdej klasy. Obecnie używam tego jako alternatywy dla wzorca register() w Django, aby powiązać klasy ModelForm z unikatowym plikiem, dla szybkiego wysyłania URL-i. – Cerin

8

Myślę, że chcesz coś takiego (niesprawdzone):

class SubclassTracker(type): 
    def __init__(cls, name, bases, dct): 
     if not hasattr(cls, '_registry'): 
      cls._registry = [] 
     print('registering %s' % (name,)) 
     cls._registry.append(cls) 
     super(SubclassTracker, cls).__init__(name, bases, dct) 

Następnie dla Pythonie 2, można powołać się to podoba:

class Root(object): 
    __metaclass__ = SubclassTracker 

dla Pythona 3

class Root(object, metaclass=SubclassTracker): 

Pamiętaj, że nie musisz trzymać atrybutu _registry, ponieważ takie rzeczy są tym, co metac ses są dla. Ponieważ już zdarzyło się, że jeden kręci się wokół ...;)

Należy również zauważyć, że warto przenieść kod rejestracyjny do klauzuli else, aby klasa nie rejestrowała się jako podklasa.

+0

Występuje błąd składniowy w typie (metaclass.root._registry.append (cls) ... Zmieniłem go na: 'cls._registry.append (cls)', który działa –

+0

@Carles To był brzydki błąd składni. Tak jak w przypadku kompletnych i całkowitych śmieci, kilka razy edytowałem, więc było kilka różnych pomysłów na kilka różnych linii, które powinny zostać usunięte.) Przykro mi z tego powodu, teraz wygląda dobrze, dzięki za wskazanie. – aaronasterling

+0

W rzeczywistości, ponieważ SubclassTracker nie zawiera żadnego zakodowanego na stałe odniesienia do typu * root *, nie ma potrzeby tworzenia tej funkcji, dlatego bezpośrednio zdefiniowałem klasę SubclassTracker i zupełnie zapomniałem o make_subtracker. Działa dobrze, dzięki! –

1

Oto coś I już zostały gry z (które działa):