2010-08-13 7 views
3

Je suis occupé à créer une métaclasse qui remplace une fonction de remplacement sur une classe par une nouvelle avec une implémentation correcte. La fonction d'origine pourrait utiliser n'importe quelle signature. Mon problème est que je n'arrive pas à comprendre comment créer une nouvelle fonction avec la même signature que l'ancienne. Comment ferais-je cela?Comment créer dynamiquement une fonction avec la même signature qu'une autre fonction?

Mise à jour

Cela n'a rien à voir avec la question réelle qui est « Comment puis-je créer dynamiquement une fonction avec la même signature que une autre fonction? » mais j'ajoute ceci pour montrer pourquoi je ne peux pas utiliser de sous-classes. J'essaye d'implémenter quelque chose comme Scala Case Classes en Python. (Pas l'aspect pattern matching seulement les propriétés générées automatiquement, eq, méthodes de hachage et str.)

Je veux quelque chose comme ceci: Pour autant que je peux voir

>>> class MyCaseClass(): 
...  __metaclass__ = CaseMetaClass 
...  def __init__(self, a, b): 
...   pass 

>>> instance = MyCaseClass(1, 'x') 
>>> instance.a 
1 
>>> instance.b 
'x' 
>>> str(instance) 
MyCaseClass(1, 'x') 

, il n'y a aucun moyen que avec des sous-classes.

+1

« remplace une fonction de talon sur une classe avec une nouvelle avec une bonne mise en œuvre » est-ce pas seulement une sous-classe? –

+0

Non. Les classes marquées avec la métaclasse auront des méthodes spécifiques remplacées par de nouvelles avec des implémentations appropriées. Les classes seront définies par les utilisateurs de la bibliothèque. Les implémentations doivent être générées dynamiquement en fonction des signatures de méthode. Comment ferais-je cela avec des sous-classes? – hwiechers

+0

"Les [sous-classes] seront définies par les utilisateurs de la bibliothèque". C'est comme ça que l'héritage fonctionne. "Les implémentations [correspondent] aux signatures de méthode" car c'est ainsi que fonctionne l'héritage. Je ne vois pas pourquoi l'héritage simple n'est pas approprié. Peut-être que si vous fournissiez du code qui prouvait que l'héritage simple ne pouvait pas être utilisé. –

Répondre

3

Je crois functools.wraps ne reproduit pas la signature d'appel d'origine. Cependant, decorator module Michele Simionato fait:

import decorator 

class FooType(type): 
    def __init__(cls,name,bases,clsdict): 
     @decorator.decorator   
     def modify_stub(func, *args,**kw): 
      return func(*args,**kw)+' + new' 
     setattr(cls,'stub',modify_stub(clsdict['stub'])) 
class Foo(object): 
    __metaclass__=FooType 
    def stub(self,a,b,c): 
     return 'original' 


foo=Foo() 
help(foo.stub) 
# Help on method stub in module __main__: 

# stub(self, a, b, c) method of __main__.Foo instance 

print(foo.stub(1,2,3)) 
# original + new 
0

Il est possible pour ce faire, en utilisant inspect.getargspecs. Il y a même un PEP in place pour le rendre plus facile. Mais ce n'est pas une bonne chose à faire. Pouvez-vous imaginer combien d'un cauchemar de débogage/maintenance ce serait d'avoir vos fonctions dynamiquement créé au moment de l'exécution - et pas seulement cela, mais fait par une métaclasse ?! Je ne comprends pas pourquoi vous devez remplacer le talon dynamiquement; ne pouvez-vous pas simplement changer le code lorsque vous voulez changer la fonction? Je veux dire, supposons que vous avez une classe

class Spam(object): 
    def ham(self, a, b): 
     return NotImplemented 

Puisque vous ne savez pas ce qu'il est censé faire, la métaclasse ne peut réellement mettre en œuvre toutes les fonctionnalités. Si vous saviez ce que ham devait faire, vous pouvez le faire dans ham ou l'une de ses classes parentes, au lieu de renvoyer NotImplemented.

0

utilisation functools.wraps

>>> from functools import wraps 
>>> def f(a,b): 
    return a+b 
>>> @wraps(f) 
def f2(*args): 
    print(args) 
    return f(*args) 
>>> f2(2,5) 
(2, 5) 
7 
Questions connexes