2010-10-06 4 views
6

J'ai lu quelques tutoriels sur les métaclasses Python. Je n'en ai jamais utilisé un auparavant, mais j'en ai besoin d'un pour quelque chose de relativement simple et tous les tutoriels semblent orientés vers des cas d'utilisation beaucoup plus complexes. Je veux fondamentalement créer une classe de modèle qui a un certain corps pré-spécifié, mais prend sa classe de base en tant que paramètre. Depuis que j'ai eu l'idée de modèles C++/D, est ici un exemple de ce que le code que je veux écrire ressemblerait en C++:Metaclass à paramétrer Inheritance

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

Répondre

9

Bien qu'il peut certainement être fait avec métaclasses, vous pouvez faire ce que vous voulez sans eux parce que dans les classes Python sont eux-mêmes des objets. Les moyens qui, étonnamment, essentiellement rien de plus qu'une traduction presque un-à-un du code C++ est nécessaire. En plus d'être relativement simple à cause de cela, il va également fonctionner sans modification dans les deux Python 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() 

Sortie:

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() 

Le code dans le (unimaginatively nommé) fonction est template() un exemple de ce qui est communément appelé un class factory ou une implémentation du modèle d'usine . Par ailleurs, vous pourriez trouver my answer à la question What exactly is a Class Factory? informative.

Edit: Ajout de code pour créer des noms de classe pour chaque sous-classe retournée, qui a été inspiré par @ un aperçu de aaronasterling (dans un commentaire maintenant supprimé) au sujet de la confusion potentielle lors du débogage si la classe fabriqué toujours le même nom .

+0

Impressionnant, je suppose que méta-classes sont trop pour quelque chose de si simple. Cette solution prend tout son sens avec le recul, car les types sont des objets de première classe en Python, et les classes peuvent être créées au moment de l'exécution. Je suppose que je ne suis pas encore assez à l'aise avec la manière dynamique de penser le langage pour arriver à moi tout seul, cependant. – dsimcha

+0

Les modèles * sont des métaclasses en C++, un langage fortement typé. Dans une version faiblement typée comme Python, où les classes sont aussi des objets comme vous l'avez noté, il n'est souvent pas nécessaire d'y aller - mais quand vous le faites, vous n'êtes pas limité aux arguments de template et pouvez faire des choses incroyables. – martineau

+6

** Python est fortement typé **, vous ne pouvez pas ajouter une chaîne et un entier en Python comme vous le feriez dans un langage faiblement typé comme Javascript. Incidemment, ** Python est également dynamiquement-tapé **. –

0

Cela n'a aucun sens en Python, car il ne possède pas de modèles. Ma compréhension des templates paramétrés en C++ (ce qui est plutôt vague, puisque cela fait plusieurs années que je les ai regardés), c'est que ça fonctionne comme une fabrique de classe, et peut créer une sous-classe de n'importe quelle classe qui a des méthodes additionnelles ou attributs ajoutés.

En Python, vous pouvez le faire avec une fonction d'usine qui prend une classe et retourne une nouvelle classe à l'exécution:

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