2

La méthode standard de faire singletons en Python estsingletons Google App Engine (Python)

class Singleton(object): 
    _instance = None 
    def __new__(cls, *args, **kwargs): 
     if not cls._instance: 
      cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) 
     return cls._instance 

Cependant, cela ne fonctionne pas sur App Engine, car il y a peut-être de nombreux serveurs et nous obtiendrions une instance par serveur. Alors, comment le ferions-nous pour une entité de moteur d'application?

Quelque chose comme:

class MySingleton(db.models): 
    def __init__(self): 
     all = MySingleton.all() 
     if all.count() > 0: 
      return all.fetch(1).get() 

     super(MySingleton, self).__init__ (*args, **kwargs) 

Cela conduit à une erreur de recusion, depuis get() appels __init__.

Comment nous allons l'utiliser:

Nous voulons simplement représenter un fichier de configuration, à savoir:

{ 'sitename': "My site", 'footer': "This page owned by X"} 
+0

Il serait plus facile de suggérer une solution si vous nous dites ce dont vous avez besoin pour un singleton. –

+0

@Nick: fait, merci. –

+0

Pourquoi utiliser un objet pour un fichier de configuration? Ayez juste un fichier .py avec des définitions de variable pour vos variables de configuration. –

Répondre

2

Si vous ne stockez pas les données dans le magasin de données, pourquoi ne créez-vous pas simplement un module avec des variables au lieu d'un db.Model?

Nommez votre fichier mysettings.py et à l'intérieur écrire:

sitename = "My site" 
footer = "This page owned by X" 

Ensuite, le module python devient effectivement un "singleton". Vous pouvez même ajouter des fonctions, si nécessaire.Pour l'utiliser, vous faites quelque chose comme ceci:

import mysettings 
print mysettings.sitename 

Voilà comment traite django avec ce avec leur DJANGO_SETTINGS_MODULE

Mise à jour: On dirait que vous voulez vraiment utiliser un db.Model, mais l'utilisation memcached afin que vous ne récupériez qu'un seul objet une fois. Mais vous devrez trouver un moyen de le vider quand vous changez les données, ou le faire passer par un délai d'attente afin de le recevoir de temps en temps. Je serais probablement aller avec la version du délai d'attente et faire quelque chose comme ça dans mysettings.py:

from google.appengine.api import memcache 
class MySettings(db.Model): 
    # properties... 

def Settings(): 
    key = "mysettings" 
    obj = memcache.get(key) 
    if obj is None: 
     obj = MySettings.all().get() # assume there is only one 
     if obj: 
      memcache.add(key, zone, 360) 
     else: 
      logging.error("no MySettings found, create one!") 
    return obj 

Ou, si vous ne voulez pas utiliser memcache, puis juste stocker l'objet dans une variable de niveau du module et toujours utilisez la fonction Settings() pour le référencer. Mais alors vous devrez mettre en place un moyen de le vider jusqu'à ce que l'instance de l'interpréteur soit recyclée. J'utiliserais normalement memcached pour ce genre de fonctionnalité.

+0

Nous avons besoin que les données soient synchronisées sur toutes les instances, nous allons donc certainement les stocker dans le magasin de données. –

+0

Il est presque certainement préférable de donner à l'objet un nom-clé et de le récupérer par la clé au lieu de faire une requête qui aura exactement le même résultat à chaque fois. – geoffspear

1

__init__ ne peut pas utilement return quoi que ce soit: tout comme dans le premier exemple, remplacez __new__ à la place!

+0

Ceci conduit à une erreur de récursion, identique à '__init__'. Bien que je n'ai jamais rencontré '__new__' avant, et les docs ne sont pas très clairs sur la façon de l'utiliser. –

+0

Je pense que les documents à http://docs.python.org/reference/datamodel.html?highlight=__new__#object.__new__ sont ** tout à fait clairs - quel problème avez-vous avec eux? Mais l'astuce pour éviter la récursivité est d'introduire une classe intermédiaire qui est sauvegardée dans le magasin - MySingleton la sous-classe, et est ce qu'on voit de tout le reste du code au niveau de l'application, mais délègue toutes les récupérations de stockage et sauvegarder dans la classe intermédiaire (vous pouvez aussi le faire dans l'autre sens) - avec un sous-classe de classe 'MySingleton' étant celui qui fait get and puts). –

+1

Ce sont les documents que nous avons examinés. C'est clair comment cela fonctionne normalement, mais pas comment cela peut nous aider. Pour le reste, je ne suis pas. Un échantillon de code aiderait énormément. –

4

Les singletons sont généralement une mauvaise idée, et je serais intéressé de voir ce qui en fait une exception. En général, ils ne sont que globaux déguisés, et en dehors de tous les vieux problèmes avec les globaux (par exemple voir http://c2.com/cgi/wiki?GlobalVariablesAreBad, en particulier le bit en haut parle de non-localité, de couplage implicite, de problèmes de concurrence, et de test et de confinement). le monde moderne vous obtenez des problèmes supplémentaires causés par des systèmes distribués et simultanés. Si votre application peut potentiellement s'exécuter sur plusieurs serveurs, pouvez-vous réellement faire fonctionner les deux instances de votre application sur la même instance de singleton, à la fois correctement et en toute sécurité?

Si l'objet n'a pas d'état, la réponse est oui, mais vous n'avez pas besoin d'un singleton, juste un espace de noms.

Mais si l'objet a un certain état, vous devez vous soucier de la façon dont les deux instances d'application vont garder les détails synchronisés. Si deux instances essaient de lire et d'écrire simultanément sur la même instance, vos résultats risquent d'être erronés. (Un singleton HitCounter qui lit la valeur actuelle, ajoute 1, et écrit la valeur actuelle, peut manquer des hits de cette façon - et c'est l'exemple le moins dommageable auquel je puisse penser.)

Je ne le connais pas du tout , donc peut-être Google App Engine a une certaine logique transactionnelle pour gérer tout cela pour vous, mais cela signifie probablement que vous devrez ajouter quelques trucs supplémentaires pour faire face à des rollbacks et autres. Donc, mon conseil de base serait de voir si vous pouvez réécrire l'algorithme ou le système sans avoir recours à l'utilisation d'un singleton.

+0

Nous voulons juste une variable globale. Google App Engine gère la sécurité, l'exactitude et la synchronisation, mais il y a quelques problèmes. Par exemple, une seule entité App Engine (vous pouvez la considérer comme un objet) peut être instanciée en tant qu'objets Python multiples. D'où la question. –

+0

Je downvoted pour la suggestion de réécrire l'algorithme. J'ai besoin d'une table de hachage accessible de n'importe où dans mon programme dans lequel je garde les paramètres. Le vocabulaire commun est un singleton (un global serait une implémentation acceptable d'un singleton si cela fonctionnait, ce qui est le cas). Je serais curieux de savoir comment tout algorithme avec l'exigence pourrait être réécrit. –

+0

Enfin, la suggestion de réécrire un système entier parce que quelqu'un sent que les singletons sont une mauvaise pratique est en soi une mauvaise pratique. Évidemment, si vous pensiez que les anciennes raisons (je présume que la réentrance est ce que vous voulez dire ici) sont valables, vous auriez pu le faire valoir. Je ne pense pas que quiconque apprendra en disant simplement que «X est habituellement une mauvaise idée» sans le qualifier au moins. (Je m'excuse de critiquer quand vous avez manifestement mis du temps dans votre réponse, mais ce genre de chose est commun sur SO - et presque tous les forums techniques - et ça me rend fou. –

0

Je ne pense pas qu'il existe un vrai objet "singleton" que vous pouvez conserver dans un environnement distribué avec plusieurs instances en cours d'exécution. Le plus proche vous pouvez venir à cela utilise memcache.

Peut-être vaut-il mieux penser moins en termes de singletons et plus en termes de cohérence des données. Pour ce App Engine fournit transactions, qui vous permettent d'intercepter les modifications dans une entité qui pourrait se produire pendant que vous travaillez avec cette entité.