2010-10-06 12 views
6

Przeczytałem kilka tutoriali na temat metaclasses Python. Nigdy wcześniej go nie użyłem, ale potrzebuję czegoś na coś stosunkowo prostego, a wszystkie samouczki wydają się być nastawione na znacznie bardziej skomplikowane przypadki użycia. Zasadniczo chcę utworzyć klasę szablonu, która ma pewne wstępnie określone ciało, ale bierze swoją klasę bazową jako parametr. Ponieważ mam pojęcia, od C++/C szablony, oto przykład tego, co kod Chcę napisać będzie wyglądać w C++:Metaclass do parametrize Inheritance

template<class T> 
    class Foo : T { 
     void fun() {} 
    } 

Odpowiedz

9

Choć na pewno można zrobić z metaclasses, możesz robić, co chcesz bez nich, ponieważ w Pythonie klasy same są obiektami. Oznacza to, że - zaskakująco - w zasadzie nic więcej niż prawie jeden do jednego tłumaczenie kodu C++ jest wymagane. Poza tym jest stosunkowo nieskomplikowane z tego powodu, to będzie również działać bez modyfikacji zarówno w Pythonie 2 & 3.

def template(class_T): 
    """Factory function to create subclasses of class_T.""" 

    class Foo(class_T): 
     def fun(self): 
      print('%s.fun()' % self.__class__.__name__) 

    Foo.__name__ += '_' + class_T.__name__ # rename the subclass to reflect its heritage 
    return Foo 

class Base1: 
    def bar(self): 
     print('Base1.bar()') 

class Base2: 
    def bar(self): 
     print('Base2.bar()') 

Foo_Base1 = template(Base1) 
print('Foo_Base1 base classes: {}'.format(Foo_Base1.__bases__)) 

Foo_Base2 = template(Base2) 
print('Foo_Base2 base classes: {}'.format(Foo_Base2.__bases__)) 

subclass1 = Foo_Base1() 
subclass1.fun() 
subclass1.bar() 
subclass2 = Foo_Base2() 
subclass2.fun() 
subclass2.bar() 

wyjściowa:

Foo_Base1 base classes: (<class __main__.Base1 at 0x00A79C38>,) 
Foo_Base2 base classes: (<class __main__.Base2 at 0x00A79DC0>,) 
Foo_Base1.fun() 
Base1.bar() 
Foo_Base2.fun() 
Base2.bar() 

Kod w (unimaginatively nazwanej) template() funkcji jest przykład tego, co zwykle nazywa się class factory lub implementacją fabrycznego wzorca . Tak więc, nawiasem mówiąc, możesz znaleźć my answer na pytanie What exactly is a Class Factory? informacyjny.

Edit: Kod Dodany do tworzenia różnych nazw klas dla każdej podklasy zwróconego-który został zainspirowany @ „s aaronasterling wglądu (w obecnie usunięty komentarz) na temat potencjalnego zamieszania podczas debugowania, jeśli klasa produkowane zawsze ma taką samą nazwę .

+0

Awesome, Chyba metaclasses są przesadą czegoś tak prostego. To rozwiązanie sprawia, że ​​z perspektywy czasu jest mnóstwo sensu, ponieważ typy są obiektami pierwszej klasy w Pythonie, a klasy można tworzyć w czasie wykonywania. Wydaje mi się, że nie mam dość komfortu z dynamicznym, językowym sposobem myślenia, który sam wymyśliłem. – dsimcha

+0

Szablony * to * metaclasses w C++, język mocno napisany. W słabo napisanym typie, takim jak Python, gdzie klasy są również obiektami, jak zauważyłeś, często nie trzeba tam iść - ale kiedy to robisz, nie jesteś ograniczony do argumentów szablonu i możesz robić niesamowite rzeczy. – martineau

+6

** Python jest silnie wpisany **, nie można dodawać łańcucha i liczby całkowitej w Pythonie, tak jak robiłbyś to w słabo napisanym języku, takim jak Javascript. Nawiasem mówiąc, ** Python jest również wpisywany dynamicznie **. –

0

Jest to bez znaczenia w języku Python, ponieważ nie ma szablonów. Moje rozumienie sparametryzowanych szablonów w C++ (co jest raczej niejasne, ponieważ minęło wiele lat, odkąd na nie patrzę), jest to, że działa jak fabryka klas i może tworzyć podklas jakiejkolwiek klasy, którą dasz, która ma dodatkowe metody lub dodane atrybuty.

W Pythonie można to zrobić za pomocą funkcji fabryki, które ma klasę i zwraca nową klasę w czasie wykonywania:

In [1]: def subclassFactory(cls): 
    ...:  class Foo(cls): 
    ...:   def fun(self): 
    ...:    return "this is fun" 
    ...:  return Foo 
    ...: 

In [2]: class A(object): 
    ...:  pass 
    ...: 

In [5]: C = subclassFactory(A) 

In [6]: C 
Out[6]: <class '__main__.Foo'> 
In [7]: c = C() 
In [9]: c.fun() 
Out[9]: 'this is fun' 
In [10]: isinstance(c, A) 
Out[10]: True