Forum >> Principianti >> variabili globali cross-module come valori di default

Pagina: 1 2 Avanti

ciao tutti,

vi espongo la mia problematica.

Ho inserito svariati parametri in un modulo chiamato cfg.py che sono per me variabili globali che serviranno in svariati altri moduli all'interno del package.

Semplificando, supponendo di aver definito una variabile A=2. in cfg.py, si definisca in un altro modulo un def come nell'esempio sotto.

import cfg

def stampa(f=cfg.A):
    print(f)


nel momento in cui io volessi cambiare a run-time la variabile globale, ad esempio scrivendo in mod iterativa cfg.A=6., nel momento in cui richiamo stampa() questa mantiene sempre f=2.0 come valore predefinito.

C'è un modo per fare questo?

grazie a tutti

No, python non funziona così.





DISCLAIMER FONDAMENTALE: tutto quanto segue è ARABOCIRILLICO se
1) non sai che cosa è una variabile
2) non sai che cosa è un oggetto
3) non sai che cosa è uno scope
(E intendo DAVVERO: non del tipo "ma sì che lo so, per chi mi hai preso...". No: intendo proprio DAVVERO).
In questo caso, davvero, lo consiglio sempre ma nessuno ci crede: smettete di programmare a caso, e seguite un buon libro passo passo.





Quanto al tuo esempio, non funziona per una ragione fondamentale, e non è neppure un problema di importare "cbf.py", e non è neanche un problema di (ri)definire la variabile dalla shell interattiva. Ecco un esempio ancora più semplificato del tuo, e che però esibisce lo stesso problema:

# modulo foo.py
var = 10
def f(x=var): 
    return x

if __name__=='__main__':
    var = 20
    print(f())
Se esegui questo codice, vedrai che f() restituisce comunque 10, anche se tu subito prima di eseguirla hai cambiato il valore di "var". Il motivo è che il valore degli argomenti di default viene valutato da python "staticamente" (a compile time) e non "dinamicamente" (a runtime). In altre parole python, quando valuta ("compila") il codice per poi eseguirlo, al momento di arrivare alla riga "def f(x=var)" attribuisce alla variabile x (locale alla funzione f) il valore *in quel momento definito* di var. E del resto, prova invece questo:

# modulo foo.py
def f(x=var): 
    return x

if __name__=='__main__':
    var = 10
    print(f())
Se esegui questo, vedrai che ottieni NameError, *anche se* tu definisci "var"... ma troppo tardi. A compile time python non lo trova, quindi si pianta. E del resto, prova invece questo:

# modulo foo.py
var = 10
def f(x=var): 
    return x

var = 20
def f2(x=var): 
    return x

if __name__=='__main__':
    var = 30
    print(f1())
    print(f2())
Vedrai che qui ottieni f1() -> 10 e f2() -> 20, *anche se* le due funzioni hanno codice identico, e pescano il valore della loro "x" dalla stessa variabile "var"... solo che la variabile "var" (che resta la stessa, beninteso) nel frattempo punta a un oggetto diverso (prima l'oggetto 10, poi l'oggetto 20)... tutto questo a compile time, ovviamente. La tua "var=30" finale viene bellamente ignorata come prima.

Questa, se provi a pensarci un po' sopra come esercizio, è la trappola complementare alla ben nota trappola degli argomenti di default mutabili (https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments o uno dei centomila link che puoi googlare sul tema). In effetti tu ottieni questo comportamento perchè "var" punta a un oggetto immutabile. Quindi, ogni volta che cambi il valore di "var", in realtà stai facendo puntare "var" a un altro oggetto (e ormai l'oggetto di prima è già stato assegnato all'argomento di default, e quello resta). Ma d'altra parte, se invece "var" puntasse a un oggetto *mutabile* per cominciare...


# modulo foo.py
var = [10]

def f(x=var):
    print(x)

if __name__ == '__main__':
    var.append(20) # o var[0] = 20, qualsiasi mutamento sul posto
    print(f())
Sorpresa! Se esegui questo, vedi che f() produce in effetti l'ultima variazione che hai fatto. Perché adesso "var" punta a un oggetto mutabile... quindi puoi mutarne il valore di "var" "sul posto", senza cambiare l'oggetto. Di contro, se muti il valore di una variabile che punta a un oggetto immutabile, stai in realtà facendo puntare la variabile su un nuovo oggetto. Quindi, in quest'ultimo codice: Python inizialmente assegna alla variabile "x" il valore della variabile "var"... ovvero, la variabile "x" adesso punta all'oggetto indicato da "var"... ma siccome questo oggetto è mutabile, puoi mutarlo sul posto senza cambiarne l'oggetto, e quindi python è ben felice di considerare la mutazione anche all'ultimo momento (a runtime).








Detto tutto questo... ovviamente quello che stai cercando di fare non ha molto senso. Gli argomenti delle funzioni esistono per un motivo: eseguire il codice della funzione a partire da valori arbitrari. Gli argomenti di default esistono per un motivo: evitare di passare l'argomento quando coincide con il valore più "normale" (quello di default).


Ora, va benissimo se il "valore arbitrario" con cui vuoi eseguire la funzione non è un literal ma è una variabile: cioè, va benissimo scrivere


a = 10
foo(a)
invece di


foo(10)
Ma appunto, se vuoi passare alla funzione un oggetto arbitrariamente definito a runtime... conserva questo oggetto in una variabile e passalo alla funzione. E' inutile fare il giro lungo intorno alla stalla, passando per una variabile intermedia (e non funziona neanche lessicalmente, come visto). Invece di fare:

a = 10
def foo(x=a): 
   # etc etc
a = 20
foo()
basta che tu faccia

def foo(x):
    # etc etc
a = 20
foo(a)




--- Ultima modifica di RicPol in data 2019-02-07 16:05:03 ---

--- Ultima modifica di RicPol in data 2019-02-07 16:05:57 ---

--- Ultima modifica di RicPol in data 2019-02-07 16:06:58 ---
https://leanpub.com/capirewxpython: Capire wxPython, il mio libro
https://pythoninwindows.blogspot.com: Python in Windows, il mio blog e le mie guide
grazie mille per la risposta (inoltre ne approfitto per ringraziarti anche personalmente perchè vedo che sei quasi sempre tu a rispondermi e mi stai davvero aiutando ;)).

La risposta è molto chiara e spiega bene il concetto.


In realtà quanto mi dici era proprio l' oggetto del problema, nel senso che normalmente non farei una cosa del genere che sembra più uno "spaghetti code" che altro, ma nel caso specifico ha la sua utilità.


La problematica è che nelle funzioni in oggetto (considera che in una sola factory ci tiro fuori più di una ventina di funzioni e molti parametri di input sono i medesimi) ci sono svariati parametri, ma per chi utilizza queste funzioni il più delle volte sono pochi i parametri effettivamente da inserire e se necessitasse di cambiare uno di questi parametri "difficilmente da cambiare" preferirebbe cambiarlo per tutte le funzioni contemporaneamente (senza ogni volta dover reinserire tutte le variabili di input da modificare).

Questo pensavo rendesse la vita più facile a chi poi utilizza queste funzioni (e anche a me medesimo per lo sviluppo di funzioni composte).

Se non è chiaro nel frattempo penso a un esempio semplice per spiegarmi meglio.




in ogni caso dunque salvando la variabile in un iterabile il problema si risolve? fondalmente con vettori unitari?


Su questo mi piacerebbe chiederti un chiarimento ulteriore su come funziona l'allocazione di memoria su elementi iterabili.

Se utilizzassi come registro un dizionario e come valori di default facessi puntare a questo dizionario presente in cfg.py? :question:

--- Ultima modifica di Bibo90 in data 2019-02-07 17:10:55 ---
> in ogni caso dunque salvando la variabile in un iterabile il problema si risolve?


Oh per carità. Ti ho fatto l'esempio solo per farti capire e (magari) riflettere, non intendevo suggerire uno strampalato hack per risolvere un problema peraltro inesistente. E poi non è "un iterabile", ma "un oggetto mutabile". Conosci la differenza? Sai citare un esempio di oggetto iterabile che però non è mutabile?





Ora, quanto al tuo problema... Di nuovo, cerchiamo di capire che cosa esiste in python e perché esiste, e cerchiamo di ragionare prima di martellare idee a casaccio. Capisco che ci si mette un po' più di tempo e di fatica, ma che vuoi farci, la programmazione è fatta così, almeno in attesa dell'interfaccia telepatica.





Prima di tutto, esistono gli argomenti di default e chiaramente non è sbagliato usarli. Un argomento di default è in sostanza una semplificazione dell'API della funzione. E' come dire all'utente: guarda, nel caso più comune puoi chiamare la funzione con questa API semplificata, e allora funzionerà lo stesso in modo "ragionevole" (con dei default, appunto). Oppure, se proprio sei in una situazione particolare per cui hai bisogno di modificare il comportamento standard, puoi comunque farlo chiamando l'API completa della funzione.


E fin qui ci siamo, no?

Secondo: è sicuramente possibile impostare l'argomento di default NON a un literal, MA al valore di una variabile. Però è anche un po' più "strano" da fare, un pochino più insolito, diciamo. Sarebbe:


a = 10
def foo(x=a):     # invece di "foo(x=10)"
    # etc etc 
Perché è più strano? Beh, prima di tutto perché abbiamo già detto (e spero che ormai sia interiorizzato) che è pericoloso da fare lessicalmente. Occorre che la variabile ("a" nel nostro caso) sia definita PRIMA della definizione della funzione, o che comunque python la trovi a compile time. Inoltre, ciò che appare come una "variabile" è invece "quasi-una-costante", nel senso che non la puoi modificare a runtime. O meglio, la *puoi* modificare a runtime *ma* il comportamento di foo non cambia... il che è molto controintuitivo e conduce a errori... del tipo che, appunto, tu cambi la variabile a runtime e poi ti stupisci che la funzione non cambia risultato.


Per questo motivo, quando si usa questo pattern, si sta attenti per lo meno a rendere chiaro che la variabile in realtà va intesa come una COSTANTE, ovvero non andrebbe modificata a runtime. Per esempio, qualcosa del genere:


HOST = 'localhost'
PORT = 8080
def connect(user, password, host=HOST, port=PORT):
    # etc etc
E inoltre, se poi uno vuole poter "configurare" il valore di queste costanti, allora certamente può raggrupparle in un modulo apposito dove è più facile vederle e modificarle all'occorrenza:

from conf import HOST, PORT
def connect(user, password, host=HOST, port=PORT):
    # etc etc
Basta capirci però: la "configurazione" qui avviene in modo rigorosamente statico, ***PRIMA*** di lanciare il programma. Uno apre il modulo "conf.py" con un bell'editor, modifica il valore di queste costanti, e ***POI*** lancia il programma. Dal punto di vista del programma, queste sono costanti.


Ora, a patto di chiarire bene che questi valori di default sono delle COSTANTI e non possono essere modificati a runtime, allora questo pattern è tollerabile. E' come dire all'utente: puoi chiamare la funzione con l'API semplificata, e allora i parametri dipendono da delle costanti che tu INOLTRE se vuoi puoi anche andare a modificare direttamente nel codice del modulo conf.py (o in un file ".ini" di configurazione, o va a sapere) per adattarle meglio al tuo ambiente di lavoro. Oppure, beninteso, puoi chiamare la funzione con tutti i parametri, dando i valori che vuoi tu e ignorando del tutto i default.





Ora, arrivati a questo punto, a scanso di equivoci, ti prego: davvero, davvero, davvero. RILEGGI TUTTO DA CAPO con calma. Dimenticati del tuo problema, concentrati su python, su questi pattern, sul MOTIVO di questi pattern. Non cercare a tutti i costi di pensare al tuo problema specifico e di incastrarlo a martellate in questi pattern. Dimenticati del tuo problema. Capisco che tutto ciò che stai pensando mentre leggi (in mezzo secondo netto) è OKMACOMELORISOLVOILMIOPROBLEMARISOLVORISOLVORISOLVORISOLVOOOOOOOOOOOOOO????? Ma davvero, fidati. E' come imparare a parlare una lingua. Devi capire come funziona, prima di applicarla alle cose che vuoi dire. E' per questo che l'unica soluzione resta di leggersi un libro. Passo passo. Con pazienza, e tanto tanto tempo.





E a questo punto: esiste il pattern per cui, oltre ai parametri di default (che vanno bene), oltre ai parametri di default espressi come costanti (che vanno un po' meno bene ma insomma), c'è anche la possibilità di esprimere i parametri di default come "variabili"? Ovvero come variabili che *davvero* succede che si possono variare a runtime?


Ok, la risposta è no, come già sappiamo: è proprio lessicalmente impossibile. Ma anche se fosse possibile, a che diavolo servirebbe? Se chi chiama la funzione (l'utente) ha anche modo di cambiare il valore delle variabili legate agli argomenti di default (e ripeto, supponiamo che funzioni lessicalmente), ALLORA però tanto vale che non utilizzi affatto gli argomenti di default, dico bene? Cioè, immagina se si potesse fare:


FOO, BAR, BAZ = 0, 0, 0  # magari in un conf.py

def f(foo=FOO, bar=BAR, baz=BAZ):
    return foo+bar+baz

# questo NON funziona... ma immaginiamo che invece si possa fare:
FOO, BAR, BAZ = 10, 20, 30
f()  # restituisce 60
FOO, BAR, BAZ = 3, 4, 5
f()  # restituisce 12
# eccetera
Ma allora sarebbe la stessa cosa passare alla funzione i valori che COMUNQUE hai già dovuto moficare e QUINDI conosci, giusto?

FOO, BAR, BAZ = 0, 0, 0  # magari in un conf.py

def f(foo=FOO, bar=BAR, baz=BAZ):
    return foo+bar+baz

# questo invece e' perfettamente logico e normale:
foo, bar, baz = 10, 20, 30
f(foo, bar, baz) # 60
foo, bar, baz = 3, 4, 5
f(foo, bar, baz) # 12
# eccetera
E' abbastanza chiaro, no? Se a runtime devi comunque impostare il valore di 3 variabili, allora è inutile sperare che esistano delle magie per cui dopo basta scrivere "f()" senza argomenti e magicamente funziona. Anche se esistessero, comunque la fatica di settare 3 variabili ormai l'hai fatta... allora a questo punto chiama "f(a, b, c)" passando le variabili che hai appena settato.





Quello che stai cercando di fare non avrebbe molto senso, ANCHE se si potesse fare. Poi io non so esattamente quale sia il tuo problema (parli vagamente di "factory" ma non so se stai parlando di quello che in genere si intende per factory, quindi non procedo oltre). Sicuramente ci sono dei pattern (tipicamente dei pattern che si esprimono meglio con la programmazione a oggetti, però: penso a Strategy, o magari a Decorator, Visitor...) e magari ci sono anche delle soluzioni di metaprogrammazione che riguardano ambiti vicini a quello che stai pensando tu... ma ho anche un po' la sensazione che tu stia cercando delle "feature automagiche cool" che in realtà non servono e/o ti complicano inutilmente le cose.




https://leanpub.com/capirewxpython: Capire wxPython, il mio libro
https://pythoninwindows.blogspot.com: Python in Windows, il mio blog e le mie guide
ok allora cerco di spiegarmi meglio perchè evidentemente mi sono spiegato male.

Innanzitutto chiarisco che per come ho per ora impostato gli script dei vari moduli, come mi dici di fare è già come funziona. Il problema non è di funzionalità ma di mero utilizzo, però andiamo per ordine.

Sempre semplificando cercando di andare alla radice del problema per factory di funzioni (differente dal classico pattern factory con i metodi di classe all'interno della classe factory) intendo una cosa del genere (un copia incolla della factory in oggetto togliendo alcune righe che non centrano con il problema):

def make_func(func): # function Factory
    @wraps(func)
    def mfunc(stop, start=cfg.START, step=cfg.STEP, *args, **kwargs):        
        if bool_op.grof(start,stop):
            raise ValueError("'end' must be greater than or equal to 'start'")
        result = rdvarray(func(stop, start, step, *args,**kwargs), step, start)
        if bool_op.noteq(result.stop,stop):
            warnings.warn("'stop' attribute is changed from {0:.3f} to {1:.3f}"
                          .format(stop,result.stop))
        return result
    return mfunc


la quale funzione make_func utilizzo anche come decoratore in altri contesti (altri moduli), in quanto come si può vedere è in realtà un decoratore.


Ora immaginamo di avere successivamente una cosa del genere (con funcmodule.py un modulo addetto a racchiudere le n funzioni base):

f1=make_func(funcmodule.f1)
f2=make_func(funcmodule.f2)
f3=make_func(funcmodule.f3)
f4=make_func(funcmodule.f4)

...

fn=make_func(funcmodule.fn)
fare cosi mi è molto comodo per gestire il codice perchè è tutto ben ordinato, inoltre se voglio poter cambiare struttura dati e o controlli particolare mi basta cambiare solo la factory senza toccare le n funzioni.

Detto ciò a livello di utilizzo se volessi cambiare, ad esempio la variabile cfg.STEP nelle funzioni (ma come potrebbero essere diverse altre che li non si vedono) dovrei fare tipo:

new_step=10.

a=f1(60,step=new_step)
b=f20(40,step=new_step)
c=f4(600,step=new_step)

...

op=operation(a,b,c,.....)
mentre quello che mi piacerebbe ottenere è:

setstep(10.)

a=f1(60)
b=f20(40)
c=f4(600)

...

op=operation(a,b,c,.....)
capisco che a livello di funzionalità non cambia nnt... ma se mi scoccio io da sviluppatore a scrivere ogni volta le possibili variabili da modificare mi immagino un utilizzatore :sarcastic:
Risulta un idea cosi sbagliata in termini di struttura del codice?


--- Ultima modifica di Bibo90 in data 2019-02-07 20:41:48 ---
Mah guarda, sinceramente non lo vedo tanto bene 'sto codice... ma è tardi, non ho tempo di analizzarlo e comunque almeno ho capito la tua domanda originale è del tutto scorrelata dal codice che posti (e per fortuna, a questo punto!). Il codice che posti è... strano come minimo. Intanto non è una factory ma è un decoratore python... che poi tu non usi nemmeno come un decoratore... boh. Cioè, intuisco quello che forse vorresti fare, ma non so proprio se ha senso come lo stai facendo, e non riesco a mettere a fuoco quel codice.





Ma come dicevo, per fortuna la tua domanda originale non è correlata a quel codice. La tua domanda era, come puoi ottenere un effetto del genere:


FOO, BAR = 10, 20 # o anche definiti in un conf.py esterno...
def f(foo=FOO, bar=BAR):
    return foo+bar

f() # restituisce 30

FOO, BAR = 100, 200
f() # tu *vorresti* che restituisse 300, adesso
Ora, questa di per sé è una domanda legittima, ma vedi bene che non c'entra nulla con quel wrapper, con quella factory, con quel decoratore o con tutte le altre diavolerie che non capisco nel tuo codice. E' una domanda che si può fare anche a proposito di una "semplice" funzione con parametri di default.


Posto che vale sempre tutto quello che ti ho detto prima (e in particolare questa grande e profonda verità su cui secondo me dovresti riflettere: se a runtime puoi modificare le variabili, allora puoi fare anche la fatica di passarle esplicitamente come parametri alla funzione, rinunciando ai parametri di default... è solo una non-feature finto-cool quella che stai cercando), posto tutto questo, la risposta è:


Probabilmente la cosa "giusta" per ottenere un effetto del genere è impacchettare questa logica in una classe: una classe ti dà effettivamente questo tipo di funzionalità:

FOO, BAR = 10, 20

class Klass:
    def __init__(self, foo=FOO, bar=BAR):
        self.foo = foo
        self.bar = bar

    def foobar(self):
        return self.foo + self.bar

k = Klass()
k.foobar()  # adesso è 30
k.foo, k.bar = 100, 200
k.foobar()  # e adesso effettivamente è 300...
Se questo ti sembra "barare", perché effettivamente non vai a modificare le variabili iniziali FOO e BAR, ma "soltanto" le variabili interne "self.foo" e "self.bar"... ebbene sì, ma questa è proprio la cosa giusta da fare... se ti ricordi il ragionamento che avevamo fatto (cioè che modificare FOO e BAR tanto non serve a nulla perché ormai python ha già "fissato" il valore degli argomenti di default).


Più di questo al momento non mi viene proprio in mente, ma fossi in te forse mi concentrerei di più a semplificare quella strana architettura che a trovare hack cool...


https://leanpub.com/capirewxpython: Capire wxPython, il mio libro
https://pythoninwindows.blogspot.com: Python in Windows, il mio blog e le mie guide
Spiegando brevemente quel frammento di codice (che ho postato solo per rispondere alla tua domanda su cosa intendassi quando parlavo di factory)... è una struttura che ho tratto per lo più da un testo (ovviamente modificata per il mio caso ma il concept è il medesimo), non l'ho "inventata"... (solitamente mi baso sempre su pattern abbastanza standard e non ho l'ego di pensare di inventare chissa cosa)... Puo sembrare un architettura "ingarbugliata" se visto solo quello stralcio di codice ma in realtà permette di tenere tutto ben separato e se dovessi fare modifiche alla struttura dati che utilizzo mi permette di non toccare le funzioni di base...Inoltre mi permette di riutilizzare librerie già scritte (mie e/o di terzi) fornendo dunque un interfaccia.... si è un decoratore e come ho scritto sopra in altri moduli lo uso proprio come tale (@make_func).. lo stesso effetto lo avrei copiano nel modulo le funzioni di base e decorandole... però in questo modo è tutto molto più ordinato tenendo le funzioni in un modulo a se (e in realtà in più moduli... ). Potrei ovviamente anche importare nell'altro modulo il decoratore però vorrei che le funzioni (decorate) fossero nel modulo principale... Da dove l'ho tratto lo chiamavano Function Factory per via che veniva utilizzata per creare uno stock di funzioni nel modo come ho postato sopra... non era riferito al pattern factory inteso con la classica classe contenente i metodi di classe.. Se hai consiglio su come migliorare ulteriormente per semplicità quella sezione di script sono tutto orecchie :D





Detto ciò, avevo pensato di utilizzare una classe come oggetto "ambiente"... ma in quel contesto pensavo di più a una struttura tipo pattern Command.. il che mi permetteva, oltre di avere "variabili d'ambiente" (tipo STEP) come attributi di classe (risolvendo il problema alla nascita) di allocare meno vettori, lavorare di più in loco (con i vantaggi computazionali che ne derivano) e di poter fare giochetti come comandi annullabili (che potrebbe tornarmi comodo per futuri sviluppi) o altro... il problema è che per molti altri aspetti era molto limitante e alla fine ho pensato di strutturarla cosi.... Inoltre all'utilizzo era molto meno intuitiva e mi giocavo tante compatibilità con altre librerie (come numpy matplotlib scipy che al momento risultano [almeno se ulteriori test non smentiscono] compatibili) ... dici che fosse meglio in questo modo? :question: :question:


che poi pensandoci potrei anche aggiungerla come funzionalità... nel senso... non rendere modificabili le variabili di default (quindi facendo inserire manualmente se serve)... dare però anche la possibilità di istanziare questo ambiente in cui poter modificare i parametri di default (oltre a implementare altre funzionalità ovviamente...) e con metodi predisposti di questa classe dare in uscita vettori compatibili con l'approccio precedente...





--- Ultima modifica di Bibo90 in data 2019-02-08 11:44:10 ---

--- Ultima modifica di Bibo90 in data 2019-02-08 11:51:56 ---
E' tutto davvero molto confuso, davvero ("Command"?! fa il paio con "Factory"?!). Per semplicità resto attaccato al tema del thread e non mi avventuro oltre...




Guarda, per l'ennesima volta, invece di continuare a fissarti su un hack per "risolvere" il tuo problema, il consiglio è di pensare alle strutture di Python, a che cosa sono, a PERCHE' sono fatte così. Ora, rispetto a quello che dicevi, in effetti una CLASSE è proprio fatta per incapsulare a) una routine di codice e b) del contesto con cui deve essere eseguita.


Una FUNZIONE, d'altra parte, idealmente è fatta per essere "stateless", per non avere un "contesto". Tu le passi dei parametri, e quella ti sputa un risultato. Capisci la differenza? Se vuoi una cosa che dipenda da uno stato e dal variare di uno stato, forse una funzione non è la struttura giusta per cominciare.


Poi, certo, si capisce, ovviamente, ci mancherebbe, per carità: una funzione PUO' accedere a uno stato esterno, come no. E' anche piuttosto facile. Tu ti incaponisci nell'idea che questo stato può essere passato sotto forma di parametri di default legati a variabili globali modificabili... una cosa che già a pronunciarla ad alta voce ti gira la testa. E comunque non può essere fatta, come ormai è stato spiegato e rispiegato.


Invece una soluzione c'è, ed è ovvia e molto semplice: se vuoi che una funzione acceda a uno stato esterno (magari variabile)... beh, lascia che vi acceda, no? Lascia perdere l'idea folle dei parametri di default "variabili"... banalmente fai:


def foobar():
    return foo + bar

foo, bar = 10, 20
foobar()  # restituisce 30
foo, bar = 100, 200
foobar() # adesso restituisce 300 
Ecco fatto, tutto qua. E se hai ANCHE bisogno di offrire la possibilità eventuale a chi chiama la funzione di far "sovrascrivere" questo stato con dei parametri, allora usa il comune pattern con None come default:

def foobar(x=None, y=None):
    if x is None:
        x = foo
    if y is None:
        y = bar
    return x + y

foo, bar = 10, 20
foobar()  # restituisce 30
foo, bar = 100, 200
foobar() # restituisce 300 
foobar(1, 2) # restituisce 3
Ora, sto dicendo che questa è una *Buona Idea*? NO, assolutamente no. E' una di quelle idee *pessime* che ti accorgi che sono pessime quando provi a T-E-S-T-A-R-E il tuo codice, e ti accorgi che dipende da una selva intricata di side-effect.


Ma tant'è... come si dice, se compila allora funziona... ehm ehm.





E con questo mi fermerei perché davvero, lasciamo perdere. Se non percepisci il "code smell" del tuo codice, non lo percepisci e basta. E va bene così. Quando poi lo percepirai, andrà bene così. Sono stadi sul cammino della vita, per dirla col filosofo. Nel frattempo, direi che quello che ho scritto risponde alla tua domanda iniziale da un punto di vista tecnico.

https://leanpub.com/capirewxpython: Capire wxPython, il mio libro
https://pythoninwindows.blogspot.com: Python in Windows, il mio blog e le mie guide
Ringraziandoti per le risposte, volevo chiederti solo un ultimo parere.. Non citando testi (io ho tratto in realtà da un testo..) ma googlando direttamente "function nested factory" tra i primi link trovi questo
https://stackabuse.com/python-nested-functions/
Come vedi già in un semplice esempio alla fine utilizza il medesimo costrutto che ho utilizzato io e che definisce factory function(in questo caso con un semplice numero... Ma che sia una funzione o un int o un float... Sempre oggetto è no?).. Nel caso in cui si utilizzi una funzione si ricade nella definizione di decoratore.... Ma perché dovrebbe essere così "confuso" ? Cioè cos ha che non va come codice? Nel mio caso mi risolve dei problemi e mi evita di scrivere codice in più... Che problemi dovrebbe darmi da farmi rimpiangere la scelta di scrivere così questo breve tratto di codice?


--- Ultima modifica di Bibo90 in data 2019-02-08 22:59:59 ---


Pagina: 1 2 Avanti



Esegui il login per scrivere una risposta.