2013-03-19 7 views
9

Alcune funzioni richiedono valori "costanti" (cioè non progettati per essere ridefiniti in un secondo momento) che non devono essere parametrizzati. Mentre gli argomenti predefiniti sono stored only once per each function, alcuni non sono molto significativi per essere inseriti come parametri (ad esempio, per far parte dello signature). Per esempio (non molto utile):In python, come memorizzare 'costanti' per le funzioni solo una volta?

def foo(bar): 
    my_map = {"rab": barType, "oof": fooType} 
    return my_map.get(bar,defaultType)() 

Ha sprecato tempo CPU e spazio RAM per ridefinire una costante di questo tipo per ogni chiamata. Alcuni altri modi sono di memorizzare costanti come le globali a livello di modulo o rendere la funzione una classe richiamabile, ma potrebbero esserci altri modi, forse?

Nel fare livello di modulo maniera globale, ho prefisso mia (inteso come) variabile costante con un "_" per mostrare che è lì non per interesse di nessuno. Ancora sento il namespace del modulo leggermente "inquinato", per non parlare della shame of using qualcosa as discouraged as globals affatto:

_my_map = {"rab": barType, "oof": fooType} 
def foo(bar): 
    return _my_map.get(bar,defaultType)() 

O il trasformarla in una classe modo. Faccio il __call__ un classmethod, per evitare la necessità di istanze che creano:

class foo: 
    my_map = {"rab": barType, "oof": fooType} 
    @classmethod 
    def __call__(cls,bar): 
     return cls.my_map.get(bar,defaultType)() 

Sono queste soluzioni Pythonic abbastanza?

Ci sono altri modi per farlo?

È anche una pratica regolare utilizzare tali "costanti"?

Nota questi oggetti nei miei esempi non sono necessariamente reali costanti, ma utilizzato (e potrebbe essere pensato) come tali dai loro scopo.

+0

Come è menzionato in una di queste risposte (e sono d'accordo con esso), probabilmente vorrai inserire un commento per renderlo più specifico che è una variabile globale, inoltre, ti aspetteresti che le persone leggano il tuo codice per avere una buona conoscenza di Python. Anche se ammetto di essere curioso di una soluzione più elegante! – PascalVKooten

+0

Le costanti globali vanno bene. È lo * stato globale * che causa problemi. – user2357112

risposta

10

Imposta come un attributo della funzione:

def foo(bar): 
    return foo.my_map.get(bar, defaultType)() 
foo.my_map = {"rab": barType, "oof": fooType} 

 

Una classe callable o una chiusura non è abbastanza semplice IMO.

+0

Puoi fornire informazioni se questo può essere fatto all'interno di una * definizione di classe *? I miei tentativi con entrambi i metodi instance,, class o static corrispondono a "AttributeError" o "NameError"; non è nemmeno ovvio che se è possibile, l'attributo della funzione può essere mantenuto 'vicino' alla definizione della funzione (in termini di righe nel file)? – n611x007

+0

Entrambi funzionano: https://gist.github.com/anossov/5411655 Non sono sicuro che sia meglio delle semplici costanti a livello di classe. –

+0

ho sbagliato, in realtà non li ho testati con metodi di istanza (pensavo di averlo fatto - quando provo a farlo con '@ staticmethod' o' classmethod', in alcuni casi dà * "AttributeError: 'instancemethod' object ha nessun attributo "* che mi ha ingannato.) Quindi il mio problema reale sembra usare i decoratori * @ classmethod * e * @ staticmethod * con questo. https://gist.github.com/naxa/5411709 – n611x007

2

Si potrebbe anche usare le chiusure:

def make_foo(): 
    my_map = {"rab": barType, "oof": fooType} 
    def foo(bar): 
     return my_map.get(bar,defaultType)() 
    return foo 
foo = make_foo() 
+0

Suggerirei anche di rinominare 'make_foo()' in 'foo()' - sebbene possa essere un 'foo' troppi ;-). – martineau

3

IMHO, non c'è niente di sbagliato con le costanti livello di modulo.

Nota che secondo PEP8, costanti dovrebbe essere tutto in maiuscolo, come questo:

_MY_MAP = {"rab": barType, "oof": fooType} 
def foo(bar): 
    return _MY_MAP.get(bar,defaultType)() 

Il modulo espressione regolare nella libreria standard utilizza questo stile e molte librerie di terze parti stabilite fare pure. Se non siete convinti, basta andare al vostro site-packages directory e grep:

egrep "^_?[A-Z]+ =" * 
+0

Se un livello globale del modulo è significativo per l'esterno, penso anche che in Python sia utile. Ma un aiutante di funzioni come questo non è molto ben strutturato in questo modo, perché solo il suo nome indica dove appartiene e il suo scopo. – n611x007

+0

Posso vedere il tuo punto. Volevo solo sottolineare che le costanti di livello dei moduli private sono relativamente comuni e probabilmente non sono considerate un anti-pattern in Python. È una questione di gusti, immagino. –

2

di fare qualcosa che sia il più autosufficiente possibile.È possibile creare un (aka functor) Classe function object e dargli un metodo __call__()o un classmethod (ma probabilmente non entrambi):

class bazType(object): pass 
class barType(object): pass 
class fooType(object): pass 

class Foo(object): 
    _DEFAULT_TYPE = bazType 
    _MY_MAP = {"rab": barType, "oof": fooType} 

    def __call__(self, bar): 
     return self._MY_MAP.get(bar, self._DEFAULT_TYPE)() 

    @classmethod 
    def foo(cls, bar): 
     return cls._MY_MAP.get(bar, cls._DEFAULT_TYPE)() 

# using classmethod 
print Foo.foo("rab") 

# using __call__ method 
foo = Foo() 
print foo("baz") 

# alternative way to use classmethod 
foo = Foo.foo 
print foo("oof") 

Ancora un'altra alternativa potrebbe essere quella di definire un staticmethod, che ho vinto Illustrare perché è così simile agli altri due - ma tu hai l'idea che spero.