2010-08-11 5 views
2

Je comprends que les fonctions peuvent avoir des attributs. Je peux donc faire ce qui suit:Est-il possible de faire en sorte que les fonctions Python se comportent comme des instances?

def myfunc(): 
    myfunc.attribute += 1 
    print(myfunc.attribute) 

myfunc.attribute = 1 

Est-il possible par tout moyen de faire une telle fonction se comportent comme si elle était une instance? Par exemple, je voudrais pouvoir faire quelque chose comme ceci:

x = clever_wrapper(myfunc) 
y = clever_wrapper(myfunc) 
x.attribute = 5 
y.attribute = 9 
x() # I want this to print 6 (from the 5 plus increment) 
y() # I want this to print 10 (from the 9 plus increment) 

En l'état actuel, il n'y a qu'une « instance » de la fonction, donc attribute existe qu'une seule fois. La modification par x ou y modifie la même valeur. Je voudrais que chacun d'eux ait son propre attribute. Est-ce possible de faire du tout? Si oui, pouvez-vous fournir un exemple simple et fonctionnel?

Il est important que je puisse accéder à attribute depuis l'intérieur de la fonction mais que la valeur de attribute soit différente selon l'instance de la fonction appelée. Essentiellement, je voudrais utiliser attribute comme s'il s'agissait d'un autre paramètre de la fonction (afin qu'il puisse changer le comportement de la fonction) mais pas le transmettre. (Supposons que la signature de la fonction a été fixée de sorte que je ne puisse pas changer la liste des paramètres.) Mais je dois être en mesure de définir les différentes valeurs pour attribute, puis appeler les fonctions dans l'ordre. J'espère que cela à du sens.

Les principales réponses semblent dire de faire quelque chose comme ceci:

class wrapper(object): 
    def __init__(self, target): 
     self.target = target 
    def __call__(self, *args, **kwargs): 
     return self.target(*args, **kwargs) 

def test(a): 
    return a + test.attribute 

x = wrapper(test) 
y = wrapper(test) 
x.attribute = 2 
y.attribute = 3 
print(x.attribute) 
print(y.attribute) 
print(x(3)) 
print(y(7)) 

Mais cela ne fonctionne pas. Peut-être que je l'ai fait incorrectement, mais il est dit que test n'a pas attribute. (Je suppose que c'est parce que wrapper a réellement l'attribut.)

La raison pour laquelle j'ai besoin de ceci est parce que j'ai une bibliothèque qui attend une fonction avec une signature particulière. Il est possible de mettre ces fonctions dans un pipeline de sorte qu'elles soient appelées dans l'ordre. J'aimerais passer plusieurs versions de la même fonction, mais changer leur comportement en fonction de la valeur d'un attribut. Je voudrais donc pouvoir ajouter x et y au pipeline, plutôt que d'avoir à implémenter une fonction test1 et une fonction test2 qui font presque exactement la même chose (sauf pour la valeur de l'attribut).

+0

Y a-t-il une raison particulière pour laquelle vous voulez une fonction et non une classe? Est-ce seulement à des fins éducatives ou y a-t-il un vrai problème que vous essayez de résoudre? –

+0

Les noms de classe doivent être PascalCase :) –

+0

Essayez ma réponse. Je l'ai testé et cela fonctionne –

Répondre

7

Vous pouvez créer une classe avec une méthode __call__ qui aboutirait à une opération similaire.

Modifier pour plus de détails: Au lieu de créer une fonction myfunc, rendez-la attrayant. Il marche comme une fonction et il fait un charlatan comme une fonction, mais il peut avoir des membres comme une classe.

+0

Je dois être en mesure d'accéder à l'attribut à l'intérieur de la fonction. Serait-ce encore possible en utilisant votre approche? – agarrett

+1

Oui; mais il utilise une classe maintenant (en tant que fonction), pas une simple fonction. –

+5

Oui, vous pouvez accéder à n'importe quel membre d'une classe. Nous sommes tous des adultes consentants ici, nous n'avons pas de membres privés;) –

0
#!/usr/bin/env python 
# encoding: utf-8 

class Callable(object): 
    attribute = 0 

    def __call__(self, *args, **kwargs): 
     return self.attribute 

def main(): 
    c = Callable() 
    c.attribute += 1 
    print c() 


if __name__ == '__main__': 
    main() 
+1

+1 par exemple, -1 pour mélanger les variables de classe et d'instance = +0 – delnan

+0

J'étais sous l'impression créant une nouvelle instance d'une classe faite une copie de dict de la classe. Ou bien comment serait-il accessible via soi? Je l'utilise juste comme un initialiseur. Voir un exemple: http://gist.github.com/519150 –

+0

Vous manquez attribut + = 1 –

0

Un générateur peut être une solution de rechange ici:

def incgen(init): 
    while True: 
     init += 1 
     print init 
     yield 

x = incgen(5) 
y = incgen(9) 

x.next() # prints 6 
y.next() # prints 10 
y.next() # prints 11 
x.next() # prints 7 

Vous ne pouvez pas creuser de retour dans le générateur et de manipuler les données bien.

1
class Callable(object): 
    def __init__(self, x): 
     self.x = x 

    def __call__(self): 
     self.x += 1 
     print self.x 

>> c1 = Callable(5) 
>> c2 = Callable(20) 
>> c1() 
6 
>> c1() 
7 
>> c2() 
21 
>> c2() 
22 
2

Une façon agréable:

def funfactory(attribute): 
    def func(*args, **kwargs): 
     # stuff 
     print(attribute) 
     # more stuff 

    return func 

x = funfactory(1) 
y = funfactory(2) 

x() # 1 
y() # 2 

Cela fonctionne parce que les fonctions sont les fermetures, donc ils saisir toutes les variables locales dans leur champ d'application; cela provoque la transmission d'une copie de attribute avec la fonction.

+0

Woo! Fonctionnel :) – Skilldrick

Questions connexes