2013-07-04 2 views
3

je me retrouve à l'écriture de code comme ceci relativement souvent:données précalculées coûteux ne sont pertinentes que pour une fonction

_munge_text_re = re.compile("... complicated regex ...") 
def munge_text(text): 
    match = _munge_text_re.match(text) 
    ... do stuff with match ... 

Seulement munge_text utilise _munge_text_re, donc il serait préférable de le rendre locale à la fonction en quelque sorte, mais si je déplacer la ligne re.compile à l'intérieur du def puis il sera évalué chaque fois que la fonction est appelée, en vain l'objectif de la compilation de l'expression régulière.

Existe-t-il un moyen de rendre _munge_text_re local à munge_text tout en évaluant son initialiseur une seule fois? L'évaluation unique ne doit pas nécessairement avoir lieu au moment du chargement du module; sur la première invocation de munge_text serait assez bon. L'exemple utilise une regex, et la plupart du temps j'en ai besoin pour une regex, mais ce peut être n'importe quelle donnée coûteuse à instancier (donc vous ne voulez pas le faire chaque fois que la fonction est appelé) et fixé pour la durée de vie du programme. ConfigParser viennent également à l'esprit. Crédit supplémentaire: Pour des raisons trop fastidieuses pour entrer ici, mon projet actuel exige une rétrocompatibilité extrême, donc une solution qui fonctionne en Python 2.0 serait meilleure qu'une autre.

Répondre

1
_munge_text_re = None 
def munge_text(text): 
    global _munge_text_re 
    _munge_text_re = _munge_text_re or re.compile("... complicated regex ...") 
    match = _munge_text_re.match(text) 
    ... do stuff with match ... 
+0

... Ce ne pas vraiment se débarrasser de la variable globale ... – zwol

+0

Non, mais il se débarrasse de l'initialisation coûteuse – Malvolio

+0

@Malvolio OP voulait cependant utiliser les locaux. –

3

Maintenant qu'il a l'état, juste faire une classe pour elle:

class TextMunger(object): 

    def __init__(self, regex): 
     self._pattern = regex 
     self._munge_text_re = None 

    def __call__(self, text): 
     if self._munge_text_re is None: 
      self._munge_text_re = re.compile(self._pattern) 

     match = self._munge_text_re.match(text) 
     # ... do stuff with match ... 

munge_text = TextMunger("... complicated regex ...") 

# The rest of your code stays the same 

Si vous ne saviez pas, la méthode __call__ sur une classe signifie que les objets peuvent être appelés comme si elles étaient des fonctions, donc vous pouvez continuer à utiliser munge_text(text) comme vous le faisiez auparavant.

(Ce genre de problème est en fait ce qui a conduit à ma question sur un lazy property decorator en Python, ce qui pourrait aussi vous intéresser, je ne se souciaient pas que si vous vous trouvez répéter ce modèle beaucoup cependant.)

+0

+1, bonne approche - mais comment faites-vous cela afin que le docstring et tel "regarde bien" à l'introspection (dans ipython/code completers/etc)? – Dougal

+0

@Dougal - bonne question. Habituellement, je ne suis intéressé que par ce que Sphinx décroche, il suffit donc de documenter la classe comme vous le feriez pour n'importe quelle autre classe. Vous pourriez essayer d'assigner 'self .__ doc__', mais cela ne sera pas dupe, par exemple. 'help (munge_text)'. Peut-être que cela fonctionnera ** pour les autres outils que vous utilisez. Sinon, postez-le peut-être comme une autre question. – detly

0

Une approche alternative - une que je ne mentionne que pour l'information, et non parce que je l'utiliserais en production, donc je vais la communauté-wiki ceci - serait de stocker l'état dans la fonction elle-même. Vous pouvez utiliser hasattr, ou prendre un AttributeError:

def munge1(x): 
    if not hasattr(munge1, 'val'): 
     print 'expensive calculation' 
     munge1.val = 10 
    return x + munge1.val 

def munge2(x): 
    try: 
     munge2.val 
    except AttributeError: 
     print 'expensive calculation' 
     munge2.val = 10 
    return x + munge2.val 

après quoi

>>> munge1(3) 
expensive calculation 
13 
>>> munge1(3) 
13 
>>> munge2(4) 
expensive calculation 
14 
>>> munge2(4) 
14 

mais pour être honnête, je passe généralement à une classe à ce stade.

+0

N.B. cela ne fonctionne pas * avec Python 2.0. – zwol

+0

@Zack: cela fonctionne avec les Pythons récents, même dans la ligne 2.x. Ou voulez-vous dire littéralement 2.0, c'est-à-dire celui qui est sorti il ​​y a plus d'une décennie? – DSM

+0

Oui, je veux dire littéralement 2.0. La rétrocompatibilité jusqu'à la version 2.0 est une exigence pour ce projet particulier. Vous seriez probablement plus heureux de ne pas connaître la raison. – zwol

0

Vous pouvez faire ce qui suit, je suppose:

def munge_text(text): 
    global munge_text 
    _munge_text_re = re.compile("... complicated regex ...") 
    def t(text): 
     match = _munge_text_re.match(text) 
     ... do stuff with match ... 
    munge_text = t 
    return t(text) 
Questions connexes