2010-12-03 37 views
7

Je ne trouvais rien sur ce sujet sur Google, donc je pense que je devrais le demander ici:Python: chaînage de la fonction jQuery?

Est-il possible d'enchaîner des fonctions avec Python, comme le fait jQuery?

['my', 'list'].foo1(arg1, arg2).foo2(arg1, arg2).foo3(arg1, arg2) #etc... 

Je perds beaucoup d'espace et de lisibilité quand j'écris ce code:

foo3(foo2(foo1(['my', 'list'], arg1, arg2), arg1, arg2), arg1, arg2) #etc... 

Il semble exister une bibliothèque pour créer ces illusive fonctions, mais je ne peux pas l'impression de voir pourquoi cela doit être si compliqué-regardant ...

Merci!

+0

Le terme est "interface fluide". Dunno pourquoi GvR ne les aime pas cependant. –

Répondre

9

est ici une extension de la suggestion de Simon ListMutator:

class ListMutator(object): 

    def __init__(self, seq): 
     self.data = seq 

    def foo1(self, arg1, arg2): 
     self.data = [x + arg1 for x in self.data] 
     # This allows chaining: 
     return self 

    def foo2(self, arg1, arg2): 
     self.data = [x*arg1 for x in self.data] 
     return self 

if __name__ == "__main__": 
    lm = ListMutator([1,2,3,4]) 
    lm.foo1(2, 0).foo2(10, 0) 
    print lm.data 

    # Or, if you really must: 
    print ListMutator([1,2,3,4]).foo1(2, 0).foo2(10, 0).data 

Vous pouvez aller un mieux et faites ListMutator agir entièrement comme une liste en utilisant le collections abstract base classes. En fait, vous pourriez sous-classer list lui-même, bien qu'il puisse vous empêcher de faire certaines choses que vous pourriez devoir faire ... et je ne sais pas ce que l'opinion générale est sur les types intégrés de sous-classes comme list.

+0

Merci! Je l'implémente maintenant dans ma bibliothèque actuelle ... – Blender

+2

Le problème avec le retour de 'self' est que c'est juste une référence dans Python si vous l'affectez à une variable différente. Dans votre exemple. Si nous ajoutons 'lm_two = lm' et faisons de nouveau' lm.foo1 (2, 0) .foo2 (10, 0) ', nous changeons les deux (' lm' et 'lm_two'.) Il est préférable de' renvoyer self .__ class__ (self.data) '. – ucyo

3

Si nous parlons de méthodes d'objet, alors c'est trivial, juste return self de chaque méthode. D'un autre côté, si vous voulez enchaîner des fonctions non liées, cela n'a pas vraiment de sens pour moi de les enchaîner comme vous le souhaitez. Bien sûr, ça a l'air sympa, mais c'est sémantiquement incohérent parce que le "." est synonyme d'accès par attribut d'objet et non de "chaîne".

+0

Hmm, donc vous dites que je * ne peux pas * définir des fonctions comme ça? Je pense que je devrais y ajouter jQuery et me définir une fonction de type 'L ('my', 'strange', 'list')'. Merci! – Blender

+0

Juste comme une sorte de question hors-sujet, quels caractères spéciaux puis-je utiliser dans un nom d'objet en Python?J'ai essayé '$', mais l'interprète n'aime pas ça ... – Blender

+0

Eh bien, techniquement, vous pourriez probablement puisque les fonctions sont aussi des objets en Python. Une fonction peut avoir un attribut personnalisé qui est aussi une fonction et qui peut être appelé comme 'func1.func2 (args)'. Mais ce serait un gâchis. – Simon

10

Tant que la fonction renvoie une valeur, vous pouvez la chaîner. Dans jQuery, une méthode de sélection renvoie généralement le sélecteur lui-même, ce qui vous permet de faire le chaînage. Si vous souhaitez implémenter enchaînant en python, vous pouvez faire quelque chose comme ceci:

class RoboPuppy: 

    def bark(self): 
    print "Yip!" 
    return self 

    def growl(self): 
    print "Grr!" 
    return self 

pup = RoboPuppy() 
pup.bark().growl().bark() # Yip! Grr! Yip! 

Votre problème, cependant, semble être que vos arguments de la fonction sont trop exigus. Le chaînage n'est pas une solution à cela. Si vous voulez condenser vos arguments de fonction, simplement attribuer les arguments à des variables avant de les transmettre à la fonction, comme ceci:

spam = foo(arg1, arg2) 
eggs = bar(spam, arg1, arg2) 
ham = foobar(eggs, args) 
+0

: D Merci beaucoup! Je ne savais même pas que ma classe pouvait faire ça! Cela fonctionne bien pour les fonctions de ce type, mais je n'arrive pas à canaliser le retour d'une fonction dans une autre ... – Blender

+0

Cela semble correspondre aux exemples ici: http: //en.wikipedia. org/wiki/Fluent_interface – Ishpeck

1

Pour référence future: jetez un oeil à Moka, une bibliothèque de programmation fonctionnelle minimaliste. À partir de leurs exemples:

(List()     # Create a new instance of moka.List 
    .extend(range(1,20)) # Insert the numbers from 1 to 20 
    .keep(lambda x: x > 5) # Keep only the numbers bigger than 5 
    .rem(operator.gt, 7) # Remove the numbers bigger than 7 using partial application 
    .rem(eq=6)    # Remove the number 6 using the 'operator shortcut' 
    .map(str)    # Call str on each numbers (Creating a list of string) 
    .invoke('zfill', 3)  # Call zfill(x, 3) on each string (Filling some 0 on the left) 
    .insert(0, 'I am')  # Insert the string 'I am' at the head of the list 
    .join(' '))    # Joining every string of the list and separate them with a space. 

>>> 'I am 007' 
+0

un meilleur lien: https://github.com/phzbox/Moka – Anentropic

0

Jetez un coup d'œil à this. C'est une classe d'encapsulation simple pour enchaîner. Et il a implémenté certaines fonctionnalités de la fonction underscore.js lib. Vous enveloppez votre liste, tuple ou dict avec un trait de soulignement, et jouez avec, puis obtenez la valeur en ajoutant un autre trait de soulignement.

print (_([1,2,3]) 
     .map(lambda x: x+1) 
     .reverse() 
     .dict_keys(["a", "b", "c"]) 
     .invert() 
     .items() 
     .append(("this is what you got", "chaining")) 
     .dict()._) 

sortie:

{2: 'c', 3: 'b', 4: 'a', 'this is what you got': 'chaining'}