2016-07-19 177 views
8

Sto cercando di scrivere una funzione Python che alla prima chiamata, restituisce un 1. Nella seconda chiamata, restituisce un 2. Il terzo, un 3. EccFunzione semplice che restituisce un numero incrementato di 1 per ogni chiamata, senza globali?

Attualmente, ho raggiunto questo utilizzando un variabile globale:

index = 0 

def foo(): 
    global index 
    index += 1 
    return index 

Quando si chiama la funzione tre volte:

print(foo()) 
print(foo()) 
print(foo()) 

Esso restituisce i valori attesi:

1 
2 
3 

Ma, ho letto che è una cattiva pratica usare le variabili globali. Quindi, mi chiedevo se lo stesso risultato potesse essere raggiunto senza l'utilizzo di globals.

Qualche suggerimento?

Grazie per il vostro aiuto.

risposta

6

È possibile utilizzare la funzione attributi:

def f(): 
    f.counter = getattr(f, 'counter', 0) + 1 
    return f.counter 

o chiusure:

def w(): 
    counter = 0 
    def f(): 
     nonlocal counter 
     counter += 1 
     return counter 
    return f 
+0

'nonlocal' è python3 solo IIRC.wrt/al trucco "attributo di funzione", non lo consiglio definitivamente, poiché è veramente fragile (aggiungi 'g = f; f = lambda: None' dopo la definizione di' f' e scoprirai perché). –

11

Utilizzando una chiusura:

def make_inc(): 
    val = [0] 
    def inc(): 
     val[0] += 1 
     return val[0] 
    return inc 

inc = make_inc() 
print inc() 
print inc() 
print inc() 

Utilizzando una classe (la soluzione più ovvia in un OOPL) :

class Inc(object): 
    def __init__(self): 
     self._val = 0 

    def __call__(self): 
     self._val += 1 
     return self._val 


inc = Inc() 
print inc() 
print inc() 
print inc() 

Utilizzando un generatore (non direttamente richiamabile, dovrete utilizzare il metodo .next()):

def incgen(): 
    val = 0 
    while True: 
     val += 1 
     yield val 


inc = incgen() 
print inc.next() 
print inc.next() 
print inc.next() 
+0

Probabilmente il tuo codice generatore dovrebbe usare 'next (inc); next (inc) 'invece di' inc.next() 'perché il primo funziona in python2.6 + e anche in python3, mentre il secondo funziona solo in python2.x – Bakuriu

+0

@Bakuriu sì scusa devo ancora passare a py3K

4

fornirò una soluzione alternativa alla risposta di Sergey: sfruttare mutable default arguments!

def f(_=[0]): 
    _[0] += 1 
    return _[0] 

Questo ha lo svantaggio che un utente potrebbe erroneamente chiamare la funzione con un argomento e non riceverà un messaggio di errore.

2

È possibile utilizzare un oggetto?

class Incrementer: 
    def __init__(self): 
     self.value = 0 

    def __call__(self): 
     print(self.value) 
     self.value += 1 

Poi si può chiamare come una funzione dopo un'istanza esso:

>>> a = Incrementer() 
>>> a() 
0 
>>>a() 
1 
1

È possibile utilizzare anche generatore.

def yrange(n): 
    i = 0 
    while i < n: 
     yield i 
     i += 1 

uscita:

>>> y = yrange(3) 
>>> y 
<generator object yrange at 0x401f30> 
>>> y.next() 
0 
>>> y.next() 
1 
>>> y.next() 
2 
>>> y.next() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 
0

si può anche migliorare generatore, renderlo più flessibile:

def counter_gen(): 
    count = 0 
    while True: 
     res = yield count 
     count += res 

>>> counter = counter_gen() 
>>> counter.send(None) #initialize generator 
>>> 0 
>>> counter.send(1) 
>>> 1 
>>> counter.send(3) 
>>> 4