2010-07-13 5 views
0

J'ai besoin d'un système de messagerie d'événements dans mon application google app engine.paresseux publier un abonnement souscrire en python

et je faisais référence à la bibliothèque python suivante.

http://pubsub.sourceforge.net/apidocs/concepts.html

ma question est, est-il indispensable que la fonction d'écoute que je veux exécuter doivent être importées (ou existeraient pas autrement) quelque part dans le chemin d'exécution afin de l'exécuter sur l'événement?

Il y a beaucoup de lots d'événements, et je veux le rendre aussi paresseux que possible.

Quel pourrait être le travail?

Y a-t-il un cadre de publication d'événements paresseux en python?

Répondre

1

tipfy (un microprogramme spécifique à App Engine) a un chargement paresseux, mais uniquement pour les «événements» spécifiques qui sont des requêtes Web que votre code est en train de traiter. D'autres frameworks web l'ont aussi, mais tipfy est petit et assez simple pour facilement étudier et imiter ses sources à cet effet.

Donc, si vous ne pouvez pas trouver un cadre d'événements plus riche qui est exactement à votre goût en raison de la question « chargement paresseux », vous pouvez choisir un qui nécessite une inscription/abonnement d'objets appelables et permettre chaînes fonctions de nommage être enregistré aussi bien que tipfy. La fonction ainsi nommée, bien sûr, serait chargée juste à temps si nécessaire pour servir un événement. Permettez-moi d'illustrer par un code simplifié et hypothétique. Dites que vous avez un cadre d'événements qui comprend quelque chose comme:

import collections 
servers = collections.defaultdict(list) 

def register(eventname, callable): 
    servers[eventname].append(callable) 

def raise(eventname, *a, **k): 
    for s in servers.get(eventname,()): 
     s(*a, **k) 

Les entrailles de tout cadre d'événements dans le monde réel sera plus riche, bien sûr, mais quelque chose comme ceci sera visible au niveau des couches les plus bas de celui-ci. Donc, cela nécessite que le callable soit chargé au moment de l'enregistrement ... et pourtant, même sans toucher aux composants internes de votre framework, vous pouvez facilement l'étendre. Tenir compte:

import sys 

class LazyCall(object): 
    def __init__(self, name): 
     self.name = name 
     self.f = None 
    def __call__(self, *a, **k): 
     if self.f is None: 
      modname, funname = self.name.rsplit('.', 1) 
      if modname not in sys.modules: 
       __import__(modname) 
      self.f = getattr(sys.modules[modname], funname) 
     self.f(*a, **k) 

Vous voulez une meilleure gestion des erreurs & c, bien sûr, mais cela est l'essentiel de celui-ci: envelopper la chaîne nommant la fonction (par exemple 'package.module.func') dans un objet wrapper qui sait comment charger paresseusement il. Maintenant, register(LazyCall('package.module.func')) va enregistrer, dans l'infrastructure intacte, un tel wrapper - et paresseux-charger sur demande. Ce cas d'utilisation, btw, pourrait être utilisé comme un exemple raisonnablement bon d'idiome Python que certains fous obsessionnels prétendent, fort et strident, n'existent pas, ou ne devraient pas exister, ou quelque chose comme: un objet changeant dynamiquement sa propre classe. Les cas d'utilisation de cet idiome sont de «couper l'intermédiaire» pour les objets qui existent dans l'un des deux états, la transition du premier au second étant irréversible. Ici, le premier état d'un appelant paresseux est "Je connais le nom de la fonction mais je n'ai pas l'objet", le second est "Je connais l'objet fonction".Depuis son de la première à la seconde est irréversible, vous pouvez couper la petite tête de test à chaque fois (ou les frais généraux de indirection du motif de conception Strategy), si vous le souhaitez:

class _JustCallIt(object): 
    def __call__(self, *a, **k): 
     self.f(*a, **k) 

class LazyCall(object): 
    def __init__(self, name): 
     self.name = name 
     self.f = None 
    def __call__(self, *a, **k): 
     modname, funname = self.name.rsplit('.', 1) 
     if modname not in sys.modules: 
      __import__(modname) 
     self.f = getattr(sys.modules[modname], funname) 
     self.__class__ = _JustCallIt 
     self.f(*a, **k) 

Le gain est modeste ici, comme il réduit simplement un if self.f is None: vérifier de chaque appel; mais c'est un gain réel, sans réels inconvénients, sauf pour faire flipper les fous obsédéperous précédemment dans leur frénésie typique de colère et de stupidité (si vous comptez que comme un inconvénient).

Quoi qu'il en soit, le choix de l'implémentation dépend de vous, pas de moi - ou, heureusement, vous ;-).

Comme un choix de conception: si patcher register lui-même pour accepter directement des arguments de chaîne (et les envelopper au besoin), essentiellement comme tipfy ne, ou aller pour l'emballage explicite sur le site d'inscription, laissant register (ou subscribe ou cependant on l'appelle) vierge. Je ne mets pas beaucoup de poids par le mantra « explicite est mieux que implicite » dans ce cas particulier, puisque quelque chose comme

register(somevent, 'package.module.function') 

est tout aussi explicite que

register(somevent, LazyCall('package.module.function')) 

-à-dire, il est assez clair ce qui se passe, et il est sans doute plus propre/plus lisible.

Néanmoins, il est vraiment agréable que l'approche d'emballage explicite laisse le cadre sous-jacent intact: où que vous pouvez passer une fonction, vous pouvez passer maintenant le nom de cette fonction (comme les paquets de noms de chaîne, le module, et la fonction elle-même), de manière transparente. Donc, si je rééquipais les cadres existants, je choisirais l'approche explicite.

Enfin, si vous souhaitez enregistrer appelables qui ne sont pas des fonctions, mais (par exemple) cas de certaines classes ou méthodes liées de tels cas, vous pouvez enrichir LazyCall dans des variantes telles que LazyInstantiateAndCall & c dans le but. L'architecture devient un peu plus complexe, bien sûr (puisque vous voulez instancier de nouveaux objets pour identifier des objets déjà existants, par exemple), mais en déléguant ce travail à un système d'usines bien conçu, il ne devrait pas être trop mauvais. Cependant, je n'approfondis pas ces raffinements, puisque cette réponse est déjà assez longue, et de toute façon, dans de nombreux cas, la simple approche "nommer une fonction" devrait suffire! -)

+0

merci. je l'ai. mais comment écouter n'importe où? (c'est-à-dire sans passer par cette fonction en cours d'exécution?) – iamgopal

+0

@lamgopal, ** HUH **? Je ne comprends pas votre question du tout. J'ai montré comment utiliser un framework ** ANY ** qui vous permet d'enregistrer des fonctions, sans charger la fonction _until_ pour servir un événement, c'est-à-dire, il est appelé, et _then_ bien sûr il est chargé ** et ** exécuté - comment pourriez-vous vouloir éviter ** que **?! L '"écoute" va se faire dans le cadre - votre fonction de service ne s'exécute que lorsqu'elle doit s'exécuter en réponse à un événement - quelles ** autres ** caractéristiques avez-vous complètement oubliées dans votre Q d'origine? Je ne peux pas imaginer tout ce qui est sensible ... pls clarifier! –

+0

désolé de ne pas l'effacer. Je n'ai pas voulu éviter l'écoute ou l'exécution. Je voulais éviter "l'enregistrement" des auditeurs, car il y en a beaucoup dans mon cas, et distribués partout dans le code.e.g. il y a un registre d'écoute dans le module x qui n'est pas importé lors de l'exécution de main à partir du module y. Si certaines fonctions du module y créent une invention, une fonction d'écoute dans le module x sera-t-elle exécutée?NON, parce que l'interprète n'y est pas arrivé. alors comment m'assurer que l'écouteur dans x est exécuté sans importer le module x au préalable pour l'enregistrer en premier lieu?, je pense qu'il n'est pas possible (au commentaire suivant) – iamgopal

Questions connexes