Le processus d'utilisation des différentes interfaces d'entrée de Sublime Text a tendance à être un enfer de rappel, alors je l'ai bien écrit en utilisant un générateur! C'est une histoire. J'utilise le plug-in que j'écris maintenant.
L'API Sublime Text 3 peut faire ce que vous voulez la plupart du temps via un éditeur de texte. Il est possible d'accepter diverses entrées telles que l'affichage d'un panneau rapide avec des éléments de menu personnalisés et l'affichage d'un panneau de saisie de texte, mais comme vous pouvez le voir, celui de l'éditeur tout en acceptant les entrées de divers utilisateurs Les autres opérations ne sont pas bloquées, non? C'est, bien sûr, parce que les fonctions qui exploitent ces interfaces de réception d'entrée sont exécutées dans un nouveau thread, mais si vous voulez interférer avec le traitement du thread principal à partir de là, après l'entrée de la fonction de rappel que vous avez passée à l'avance. Appeler avec.
Par exemple, dans le cas de l'affichage du panneau rapide, cela ressemble à ce qui suit:
from sublime_plugin import WindowCommand
class ShowMenu(WindowCommand):
def run(self):
menu = ["A", "B", "C"]
def cb(index):
print(menu[index])
self.window.show_quick_panel(menu, cb)
Peut afficher un menu contenant les éléments "A", "B", "C", et la fonction cb ()
reçoit l'index du menu sélectionné. Eh bien, ce n'est pas un gros problème car le contenu du callback est petit, mais si le nombre de lignes du callback et le nombre de variables à passer au callback augmentent, cela redeviendra gênant.
Encore plus gênant est lorsque vous devez appeler des commandes qui attendent une entrée utilisateur successivement, et lorsque vous essayez d'appeler la prochaine commande d'attente d'entrée utilisateur avec un rappel approprié dans le rappel ... Par exemple:
class Auth(WindowCommand):
def run(self):
self.window.show_input_panel("username", "", cb1)
#Lorsque le panneau de saisie s'affiche, la ligne de ce côté continue sans attendre l'entrée.
#Traitement d'écriture en utilisant l'entrée dans le rappel
def _cb1(username):
# show_input_panel()S'il est laissé tel quel, la chaîne de caractères d'entrée sera affichée et c'est vraiment ça, mais pour le moment je m'en fiche
self.window.show_input_panel(
"password", "", functools.partial(_cb2, username))
def _cb2(username, password):
_auth(username, password)
def _auth(username, password):
#Processus d'authentification à l'aide du nom d'utilisateur et du mot de passe
Eh bien, je peux l'écrire, mais ce n'est pas facile à lire. Il est fastidieux et peu intuitif de devoir se séparer en tant que fonction juste pour l'exécuter en tant que rappel, même si l'unité à découper en tant que fonction n'est pas appropriée. S'il y a beaucoup de paramètres à passer, cela sera gênant et la maintenabilité sera réduite.
Ne pouvez-vous pas écrire plus élégamment? ……Peut écrire. Avec Python.
En principe, ce serait bien si l'exécution de la fonction pouvait être suspendue lorsqu'il s'agissait d'attendre une entrée de l'utilisateur. ――Oui, c'est là que l'instruction yield
entre en jeu. En appelant l'instruction yield
, vous pouvez enregistrer l'état de fonctionnement jusqu'à ce que la fonction next ()
ou la fonctionsend ()
soit appelée pour le générateur. S'il y a une entrée de l'utilisateur, elle peut être utilisée dans l'expression du générateur en utilisant la fonction send ()
. Sinon, appelez simplement next ()
. Alors, tout d'abord, je vais préparer un décorateur comme celui-ci.
def chain_callbacks(
f: Callable[..., Generator[Callable[Callable[...]], Any, Any]
) -> Callable[..., None]:
@wraps(f)
def wrapper(*args, **kwargs):
chain = f(*args, **kwargs)
try:
next_f = next(chain)
except StopIteration:
return
def cb(*args, **kwargs):
nonlocal next_f
try:
if len(args) + len(kwargs) != 0:
next_f = chain.send(*args, **kwargs)
else:
next_f = next(chain)
next_f(cb)
except StopIteration:
return
next_f(cb)
return wrapper
C'est un peu délicat, mais vous pouvez l'utiliser pour "intégrer" vos rappels.
Dans l'instruction yield
, passez" une fonction qui prend une fonction de rappel à laquelle vous voulez que l'instruction yield
et les instructions suivantes soient exécutées lors de l'appel". Si une valeur est passée à cette fonction de rappel, elle peut être reçue comme valeur de retour de yield
.
from functools import partial
class Auth(WindowCommand):
@chain_callback
def run(self):
# `functools.partial()`Avec`on_done`Ne prenez qu'un seul argument
#Sous la forme d'une fonction, le rendement
username = yield partial(
self.window.show_input_panel, "username", "",
on_change=None, on_cancel=None)
password = yield partial(
self.window.show_input_panel, "password", "",
on_change=None, on_cancel=None)
#De là, le processus d'authentification à l'aide du nom d'utilisateur et du mot de passe
Ce côté est rafraîchissant!
Cette utilisation de générateurs est également utilisée dans les frameworks Web comme «Twisted» et «Tornado».
Recommended Posts