Closed jaanos closed 6 years ago
further_iter
in should_stop
smiselno nastavljati globalno ali je to bolj odvisno od primera do primera?update_env
ni potreben. Če je nekdo uporabil Check.set_environment
, ga verjetno želi uporabiti. Ali sem kaj zgrešil?further_iter
in should_stop
sta najbrž res odvisna od primera do primera, ampak mogoče bo kdo hotel imet več testov z isto nastavitvijo, tako da, zakaj pa ne?locals()
in ga dopolni s parametrom env
. Tako se env
nikoli ni spreminjal, in trenutna privzeta vrednost za update_env
poskrbi za enako vedenje (kar je koristno, če hočemo npr. več testov izvest z istim začetnim okoljem). Sem pa locals()
odstranil, ker se s tem v okolje prinese lokalno okolje metode (česar pa seveda nočemo).Še to: za končnega uporabnika je morda relevanten tudi Check.set_stringio
, ki nastavi razred, s katerim se vstavlja input (sem dodal v zgornjem opisu) - potem ni potrebno pri vsakem Check.input
nastavljat parametra visible
. Ampak mogoče to ni najbolj posrečeno ime - bi bilo bolje Check.set_visible
, pa se morda kar ohrani trenutna funkcionalnost (tako ali tako bo parameter večinoma True
ali False
)?
Hmm. Kaj pa če bi tudi set_stringio
, set_visible
, set_clean
in podobno dali v Check.set
. Argumente za clean bi lahko tako kot v Djangu podali z Check.set(clean__digits=6)
, ali kaj takega.
Drugače se bojim, da postaja vse skupaj malo preveč zapleteno. A bi vprašali še koga drugega za mnenje?
set_stringio
po mojem lahko pridružimo, za set_clean
pa nisem tako prepričan - se mi namreč zdi bolj čisto, če imamo metodo, ki nastavi ustrezen klic funkcije clean
, kot pa da ena metoda to naredi, če so prisotni določeni parametri. Je pa tudi res, da bo kdo lahko naredil Check.set(digits = 2)
in se čudil, zakaj ne deluje...
Še kakšno mnenje bi pa sigurno bilo dobrodošlo.
Spremljam sicer ta razgovor, ampak sem se zgubil in pravzaprav ne vem točno, zakaj gre :-((. vsaj meni osebno bi zelo pomagalo, če bi videl kak konkreten primer uporabe - torej nalogo, rešitev in s temi novostmi pripravljene teste.
Ideja je, da bi lahko z uporabo with
nastavljali okolje, funkcijo clean
in druge nastavitve, ki jih potem ne bi rabili ponavljati ob vsakem klicu metod iz razreda Check
, npr.
with Check.set_environment(x=42, y=23): # nastavimo okolje pri izvajanju izrazov
Check.equal('sestej(x, y)', 65)
Check.equal('odstej(x, y)', 19)
with Check.set_clean(digits=3): # nastavimo število decimalk za natančnost primerjavi
Check.equal('izracunaj_pi()', math.pi)
Check.equal('izracunaj_e()', math.e)
with Check.set(should_stop=True): # generatorji se morajo ustaviti po želenem številu korakov
Check.generator('naravna_stevila(3)', [1, 2, 3])
Check.generator('naravna_stevila(5)', [1, 2, 3, 4, 5])
with Check.input(["vhod", "pericarežeracirep"], visible=True): # vhod naj se izpiše oz. ga vidi Check.output
Check.output('zrcali_vhod()', ['> vhod', 'dohv'])
Check.output('zrcali_vhod()', ['> pericarežeracirep', 'pericarežeracirep'])
Predvsem 1, 2 in 4 se mi zdita zelo uporabna. Sicer za 4 si zadevo razlagam takole:
Check.output('zrcali_vhod()', ['> vhod', 'dohv'])
Verjetno je mišljeno, da je znotraj zrcali_vodi
ukaz
input("> ")
In da je v funkciji le en input (drugega "pobere" drug Check.output)
=====================
Kaj se zgodi pri gnezdenju, npr.:
with Check.input(["vhod", "pericarežeracirep"], visible=True):
with Check.input(["vhod", "pericarežeracirep"], visible=True):
ali pa
with Check.input(["vhod", "pericarežeracirep"], visible=True):
with Check.set_environment(x=42, y=23):
with Check.set_clean(digits=3):
Predvidevam, da ni težav?
Tako, ja. Implementacija je pa taka, da imamo sklad nastavitev: pri vsakem with
se na vrhu ustvari kopija trenutnih nastavitev z želenimi spremembami (set_environment
poskrbi, da se nove spremenljivke dodajo k trenutnim); pri izstopu iz with
se nastavitve z vrha sklada odstranijo in tako se vrne isto stanje kot pred vstopom.
Glede Check.input
pa je že prej bilo tako, da StringIO
nadomesti karkoli je bilo prej nastavljeno za stdin
, na koncu se pa potem vrne prejšnji objekt. V gnezdenem with
bo torej viden samo trenutni vhod; po izstopu se potem nadaljuje vhod iz zunanjega with
.
Opazil sem, da se to zgodi v Python3:
>>> eval('[slovar[x] for x in [1, 2]]', globals(), {'slovar': {1: "a", 2: "b"}})
...
NameError: name 'slovar' is not defined
Za nas to pomeni, da s set_environment
(ali s parametrom env
) ne moremo nastaviti spremenljivk, ki se pojavijo v izpeljanih izrazih (ali pa funkcijah, lambdah in razredih). Še huje, tudi če se v izrazu (za exec
) neki spremenljivki zamenja vrednost, se to opravi v lokalnem okolju in izpeljani izrazi tega ne vidijo (pri funkcijah se da vsaj priti okoli z global
).
~Vem, da bi taka sprememba v tej točki prinesla same probleme, ampak a ne bi bilo bolj smiselno, da npr. Check.equal
dobi kot argument kar rezultat klica izraza (torej ne izraza kot niz) in potem s tem dela naprej? V veliki večini primerov namreč to zadostuje - ko pa ne, pa bi lahko imeli eno funkcijo za izvajanje kode v drugačnih okoljih. Glede na to, da se preverjanja delajo na globalni ravni pri urejanju nalog, a na ravni funkcije pri reševanju, se pri trenutnem načinu dejansko pojavijo razlike: spremenljivke, nastavljene v testih, so vidne za eval
/exec
pri urejanju nalog (kjer jih dobi iz globals()
), ne pa tudi pri reševanju (razen, če se jih eksplicitno navede pod env
- kot rečeno zgoraj, pa potem še vedno ne delujejo v izpeljanjih izrazih itd.).~
Ne razumem, kaj pomeni, da v veliki večini primerov zadostuje že rezultat izraza. V veliki večini primerov namreč želiš izpisati, pri katerem izrazu pride do napake, kajne?
Ah, ne vem, kaj sem razmišljal, ko sem to pisal - očitno sem bil že precej zmeden:) Tako da sem zadnji odstavek zgoraj kar prečrtal. Še vedno pa ostaja problem iz prvega odstavka.
Kaj pa, če bi uporabljali samo en slovar, torej:
>>> env = globals()
>>> env.update({'slovar': {1: "a", 2: "b"}})
>>> eval('[slovar[x] for x in [1, 2]]', env)
['a', 'b']
A kje v testih potrebujemo razliko med globals
in locals
?
Verjetno bi šlo tako, ja. locals
se v bistvu nanaša na kontekst znotraj funkcije (ali drugega izraza) tako da ga po mojem za potrebe klicanja testov niti ne bi rabili.
Sem zdaj odstranil uporabo lokalnih okolij. Posledično sem odstranil use_globals
, update_env
pa zdaj deluje malo drugače. Če je True
, se spremenljivke iz env
dodajo v globals()
(pred vsakim eval
ali exec
), in se to uporabi za poganjanje (in se torej lahko tudi spremeni). V nasprotnem primeru se naredi kopija globalnega okolja in posodobi z vrednostmi v env
(kar je tudi privzeto delovanje).
Mimogrede, še ena zanimivost: če funkcija specificira spremenljivko z global
, se uporabi spremenljivka iz Pythonovega globalnega okolja - ne glede na to, kaj je podano kot globalno okolje v eval
/exec
.
Meni se zdi načeloma v redu. Je kar velika zadeva, tako da nisem čisto prepričan, da se ne bo nič polomilo, ampak po mojem lahko probamo in potem popravljamo.
V redu - dobro bi bilo preveriti, če so kje kakšni testi, ki uporabljajo use_globals
ali update_env
, ker morda ne bodo več delovali pravilno.
Kot sva z @matijapretnar govorila v #172, je tukaj še implementacija sklada nastavitev za Python. Za uporabnika so relevantni sledeči context managerji:
Check.set(**kwargs)
postavi nastavitve na želene vrednosti:encoding
: kodiranje znakov priCheck.in_file
inCheck.out_file
(sem odstranil priCheck.input
, ker se ne uporablja);further_iter
: število nadaljnjih korakov priCheck.generator
;should_stop
: ali najCheck.generator
preveri, če se generator ustavi;update_env
: novo - ali najCheck.ouput
inCheck.run
posodobita lokalno okolje (na skladu ali podano v parametru);use_globals
: ali najCheck.ouput
in (po novem)Check.run
lahko popravljata globalno okolje (če ja, se lokalno okolje ne uporabi).Check.set_clean(clean = None, **kwargs)
nastavi funkcijoclean
na podano funkcijo (privzetoCheck.clean
) s podanimi parametri (digits
intyped
za privzeto).Check.set_environment(**kwargs)
nastavi novo lokalno okolje, kjer se podane vrednosti spremenljivk dodajo k trenutnemu okolju.Check.set_stringio(stringio)
nastavi razred, ki se uporabi zastdin
priCheck.input
-True
nastaviVisibleStringIO
(glej spodaj),False
nastaviStringIO
,None
pa ne spremeni nastavitve. Lahko se pa poda kar razred, ki naj se uporabi.Za nastavitev svežega okolja se lahko uporabi
Check.set(env=okolje)
, kjer jeokolje
podano s slovarjem? Bo tako v redu, ali naj naredim novo metodo za ta namen?Odstranil sem funkcijo
visible_input
znotrajoutput
in namesto tega definiral razredVisibleStringIO
, ki deduje odStringIO
in izpiše to, kar se prebere. Tako se lahko s parametromvisible
priCheck.input
kontrolira, ali se bo podani vhod izpisoval (oziroma ali ga bo videlCheck.output
).Odstranil sem tudi uporabo
locals()
- znotraj funkcije to namreč vrne slovar lokalnih spremenljivk, česar pa nočemo dajat testnim primerom.