Tervehdys ja hyvää uutta vuotta kaikille.
Mistähän seuraavassa luokassa ilmenevä virhe johtuu ja miten sen voisi korjata?
class Payment: def __init__(self, number_of_units): self.number_of_units = int(input('Give no of units: ')) def display_cross_income(self): #this function works fee = 8 cross_income = (self.number_of_units * fee) print('Your fee is:',cross_income) return def display_net_income(self): #error message: net_income = (x - (1 - (self.tax_percent))) #TypeError: unsupported operand type(s) for -: 'NoneType' and 'float' tax_percent = 0.25 #the function called returns None for variable x below x = self.display_cross_income() net_income = (x - (x - tax_percent)) print('\nYour cross income is: ', net_income) return #instance of class Payment fee1 = Payment(10) #fee1.display_cross_income() fee1.display_net_income() print(fee1)
Kuten virheilmoituksessa selvästi lukee: et voi laskea miinuslaskua, jonka osapuolet ovat NoneType (eli None-arvo) ja float (eli liukuluku). Olet itse kirjoittanut koodiin kommentin ”the function called returns None for variable x below”. Olisi kiinnostavaa kuulla, miten olet saanut selville näin paljon mutta et kuitenkaan saa ongelmaa korjattua – voitko selittää, kiitos! Pitäisi olla sanomattakin selvää, että kommenttisi kertoo ongelman syyn: Koodissasi x on None, ja siksi miinuslaskua ei voi laskea. Koodissasi x on None, koska funktio palauttaa arvon None, koska et ole kirjoittanut funktion return-riville mitään muuta arvoa palautettavaksi. Lisätietoa löytyy Python-oppaasta kohdasta Omat funktiot, Palautusarvo.
Lisäksi lähettämäsi koodi ja virheilmoitus eivät edes vastaa toisiaan, koska virheilmoituksessa sinulla on x - (1 - self.tax_percent) mutta koodissa x - (x - tax_percent). Kerro aina todelliset virheilmoitukset siitä koodista, jonka oikeasti lähetät!
Tervehdys,
kiitoksia vastauksesta. Virheilmoitukseen oli jäänyt vanha ilmoitus aiemmasta koodista. My bad.
Jos annan retun lausekkeille palautettaviksi cross_income ja net_income, niin virheilmoitus kyllä poistuu, mutta othjelma tulostaa yksikkömäärällä 12 seuraavan tuloksen:
Your cross income is: 0.25
<__main__.Checking_payment object at 0x0000010A167713A0>
Sitten pitää varmaan kerrata tavallisia laskutoimituksia, nimittäin tämä hämmästyttävä lopputulos johtuu ihan matematiikasta:
x - (x - tax_percent) = x - x + tax_percent = tax_percent.
Eli tämän laskun tulos on x:n arvosta riippumatta tax_percent eli 0,25.
Jälkimmäinen tulostettava rivi johtuu siitä, että koodissasi on rivi print(fee1) ja fee1 sisältää tällaisen Checking_payment-objektin (taas kerran eri nimi kuin yllä koodissasi – toistan: lähetä oikea koodi ja oikeat virheilmoitukset). Tämän tuloksen ei pitäisi mitenkään yllättää: print tulostaa juuri sen, mitä sille annetaan, ja mikään aiempi laskulauseke tai funktiokutsu ei liity tuohon viimeiseen print-riviin.
Koodisi on lyhyt ja yksinkertainen, joten sinun pitäisi ongelmatilanteissa soveltaa siihen virheenetsinnän perusperiaatetta: kun tulee virhe, lue koodia rivi kerrallaan ja mieti ihan täsmälleen, mitä se rivi tekee. Älä mieti, mitä sen ehkä pitäisi tehdä, vaan mieti ihan todellista senhetkistä koodia, lue koodirivi ja laske sen tulos päässäsi. Tarvittaessa voit vaikka pitää paperilla kirjaa eri muuttujien arvoista.
Suosittelen myös, että kun koodissa on virhe, älä lähde muuttelemaan koodia satunnaisista kohdista vaan selvitä loogisesti se virheen syy. Esimerkiksi nyt on nähty viestiesi perusteella jo luokan nimen vaihtuminen (Payment vs. Checking_payment) ja muuttujan vaihtuminen (tax_percent vs. self.tax_percent), joilla ei ollut kuitenkaan mitään loogista yhteyttä ongelman ratkaisemiseen. Ylimääräiset muutokset vain sekoittavat tilannetta, kun ensinnäkin jokin muutos voi aiheuttaa uusia bugeja ja toiseksi auttajat eivät tiedä koodin todellista sisältöä, kun se muuttuu joka viestin välillä.
Metabolix kirjoitti:
Kun koodissa on virhe, älä lähde muuttelemaan koodia satunnaisista kohdista vaan selvitä loogisesti se virheen syy. Esimerkiksi nyt on nähty viestiesi perusteella jo luokan nimen vaihtuminen (Payment vs. Checking_payment) ja muuttujan vaihtuminen (tax_percent vs. self.tax_percent), joilla ei ollut kuitenkaan mitään loogista yhteyttä ongelman ratkaisemiseen.
Tähän liittyen myös aina pitää laittaa näytille se todellinen koodi eikä "siistiä" koodia ennen tänne laittamista. Monta kertaa on nähty se, että joku idiootti laittaa näytille ihan eri koodin, jossa kyseistä virhettä ei edes ole, koska se on "siivottu" pois foorumia varten.
Mitään luokkia, funktioita tai muuttujia ei saa nimetä eri tavalla foorumia varten eikä myöskään koodia jäsennellä uusiksi. Jos koodia haluaa siivota tällä tavoin, niin silloin tulee myös ajaa se koodi ennen jakamista ja varmistaa, että sama virhetilanne toistuu. Tällöin myös tulee liittää viesteihin kyseisen koodiversion tuottamat virheilmoitukset eikä sen toisen version.
Tervehdys,
kiitoksoa vastauksista. Yritän pitää asian mielessä.
Ohessa viimeksi osittain toimimaan saamani siivoamaton koodi ja sen virheilmoitus.
Kyse lienee siitä miten gui-luokasta saadaan yhditettyä entry ja teksti-elementit toisen luokan sisällä. Liitän myös oheen painikkeen määrittelyt, johon functio display_net_income pitäisi saada yhditettyä:
class Checking_payment: def __init__(self): pass def display_cross_income(self): #\Net_incomeClass version.py", line 267, in display_cross_income number_of_pages = float(self.gui.ent.get()) #AttributeError: 'Checking_payment' object has no attribute 'gui' number_of_pages = float(self.gui.ent.get()) checking_fee = 8 cross_income = (number_of_pages * checking_fee) gui.self.txt.insert('end', 'Checking fee is:',cross_income) return cross_income def display_net_income(self): tax_percent = 0.25 x = self.display_cross_income() net_income = (x - (x * tax_percent)) self.gui.txt.insert('end', '\nNet income is: ', net_income) return net_income fee1 = Checking_payment() fee1 = fee1.display_net_income()
Tässä painike:
self.button2 = ttk.Button(self.frm_btn, style='TButton', text='Net income', underline=0,command=lambda: self.checking_payment.display_net_income()) self.button2.grid(row=0, column=0, padx=10, pady=5, ipadx=50) self.button2.focus_force()
Virheilmoitus tarkoittaa sitä, että luokassasi ei ole gui-nimistä jäsenmuuttujaa, joten et voi sellaiseen myöskään viitata.
Eli sinun pitäisi lienee lisätä luokkaan muuttuja nimeltään "gui"
class xxx: gui = GUI() #jotenki näin varmaa
Toinen vaihtoehto on määritellä muuttuja funktiossa:
def __init__(self):
def __init__(self, jotain): self.gui = GUI(jotain?) # tai näin
Edit: myös seuraavalla rivillä on luultavasti virhe:
gui.self.txt.insert('end', 'Checking fee.... -> self.gui.txt.insert('end', 'Checking fee
Edit2: Miten tuo ohjelmasi tarkalleen rakentuu?
Olettaisin, että pitäisi toimia seuraavasti: tuon luokan, jossa sinulla on ongelmia tarvitsee palauttaa tiettyä dataa, joka tarkistetaan/lasketaan tiettyjen parametrien mukaisesti(joita et tällä hetkellä välitä yhtäkään koko luokalle, etkä yhdellekään jäsenfunktiolle) ja sitten Gui-Luokka vain siirtää tuon datan näkyville.
Nämä yhdistetään siten, että Gui-luokalta luetaan parametrit(textbox tms) -> pääohjelmassa kutsutaan tämän luokan objektia luetuilla parametreilla -> objekti/funktio palauttaa dataa pääohjelmalle -> pääohjelma siirtää datan GUI:lle -> GUI piirtää datan näytölle.
Jos kirjoitin ihan paskaa niin muut korjatkoon minua.
Tämä gui vs. self.gui (tosin eri nimillä) on ollut virheenä sinulla jo ennenkin, joten vähän ihmetyttää, minne ne edelliset ratkaisut ovat kadonneet vai mistä kiikastaa. Asia ei ole monimutkainen: Jos määritellään tavallinen muuttuja kuten gui, se on olemassa vain kyseisen funktion sisällä. Jos määritellään oliolle jäsen kuten self.gui, sitä voi käyttää jatkossa saman olion kaikissa funktioissa. Jos olio on tallennettu johonkin toiseen muuttujaan, kyseisen muuttujan kautta pääsee käsiksi myös olion jäseniin. Jos siis GUI-olion sisällä on olemassa self.txt ja Checking_payment-olion sisällä on olemassa self.gui, niin jälkimmäisessä kohdassa on käytettävissä self.gui.txt. Se on sitten toinen asia, miten tämä kokonaisuus kannattaa rakentaa, ja The Alchemist ystävällisesti tekikin esimerkin kokonaisen pienen ohjelman suunnittelusta.
Tervehdys,
kiitos vastauksesta.
gui on käyttöliittymäluokan nimi, eli ohjelmassa on class Gui:, jonne käyttöliittymän osat on määritelty.
Nyt luokassa class Checking_payment: pitäisi yhditää käyttöliittymästä entry ja text widgetti tässä luokassa, sekä yhdistää funtio display_net_income painikkeeseen button2 käyttöliittymässä.
Mikä tuossa nyt tuottaa ongelmia?
Oletko lukenut The Alchemistin esimerkkiä? Siitä käy ilmi, että järkevässä suunnittelussa Gui-oliota ei välitetä muille luokille vaan päinvastoin, eli ohjelman logiikka pidetään erillään käyttöliittymästä. Ratkaisun pitäisi siis näyttää idealtaan tältä:
class GUI: def __init__(self, checking_payment): self.checking_payment = checking_payment self.create_form() def create_form(self): # ... self.net_income_button = tk.Button(text = 'Net income', command = lambda: self.display_net_income()) def display_net_income(self): net_income = self.checking_payment.get_net_income() txt = 'Net income is: %.2f' % net_income self.net_income_txt.insert('end', txt) class Checking_payment: def __init__(self): self.cross_income = 1000 self.tax = 0.25 def get_net_income(self): return self.cross_income * (1 - self.tax) def main(): checking_payment = Checking_payment() gui = GUI(checking_payment) # ...
Säilytin tässä selvyyden vuoksi valitsemasi nimen checking_payment, vaikka en ymmärrä, mitä se yrittää kuvata. Kuten aiemmassa keskustelussa sanottiin, tämä luokkajako ei näytä järkevältä logiikan kannalta. Yhden henkilön verotus voisi hyvin olla esim. luokassa HenkilönVerotus.
Edelleen myös kannustaisin tekemään jonkin yksinkertaisemman ohjelman ilman graafista käyttöliittymää, niin voisit miettiä yksinkertaisemmissa puitteissa, miten asioita saa välitettyä funktiolta ja luokalta toiselle.
Tervehdys,
kiitos vastauksesta.
Käyttöliittymäohjelmointi kiinnostaa minua erityisesti ja olen tehnyt yksinkertaisen graafisen peruskäyttöliittymän johon voin listä funktion, luokan tai useamman niistä ja saada tulokset näkyviin konkreettisesti.
Eli uutta sovellusta varten minun pitää vain lisätä funktiot, luokat ko. käyttöliittymään ja testata niiden toimintaa konkreettisesti siinä. Tämä näyttää olevan iselleni paras tapa oppia.
Ohjelman idea on se, että tietyn tarkastustehtävän tekemisestä minulle maksetaan yksikköperusteinen palkkio, josta peritään normaali vero tietyn yksikköhinnan perusteella. Ohjelma siis laskee ensimmäisessä luokan funktiossa palkkion suuruuden yksikkö* 8 euroa ja printtaa teksti-ikkunaan palkkion suuruuden.
Toinen funktio vähentää tästä palkkiosta verotuksen osuuden, jolloin saadaan käteen jäävä summa.
Kyse ei siis ole henkilöverotuksesta sinänsä.
Koko ohjelman idea on yhtäältä laskea sillä käteen jäävä osuus, toisaalta oppia se, miten toisessa luokassa käytetään jotain käyttöliittymän osaa, esim. luetaan entry-ikkunasta yksiköiden määrä(funktio 1) ja syötetään tulos teksti-ikkunaan.
Eli ei kannata miettiä tässä kohtaa luokkajaon järkevyyttä tai sen nimeä, vaan olisin paljon autettu, jos oppisin sen, miten käyttöliittymän osia kyteketään eri luokkien välille. Toivottavasti tämä selvensi asiaa.
ma08 kirjoitti:
miten toisessa luokassa käytetään jotain käyttöliittymän osaa, esim. luetaan entry-ikkunasta yksiköiden määrä(funktio 1) ja syötetään tulos teksti-ikkunaan.
Tässä kiteytyykin se virhe, joka nyt pitää korjata. Ajattelet väärään suuntaan. Ei ole tarkoitus käyttää widgettejä muista luokista. Se on yleisen näkemyksen mukaan huonoa ohjelmointia.
Toistan asian vielä pari kertaa eri sanoin, jotta se tulisi selväksi:
Sinun versiossasi GUI sisältää tekstikenttiä rahaan liittyen ja Checking_payment käyttää näitä asioita GUI:sta, jolloin molemmat luokat ovat riippuvaisia toisistaan. Sinun versiossasi Checking_payment ei voi toimia ilman GUI-luokkaa, ja toisaalta GUI-luokassa on osia, joista ei ole hyötyä ilman Checking_payment-luokkaa. Tämä on huonoa suunnittelua. Parempi tapa on se, että toinen luokka (tässä tapauksessa Checking_payment) toimii täysin itsenäisesti ilman käyttöliittymää ja riippuvuus on vain yhteen suuntaan eli GUI tarvitsee Checking_payment-luokkaa tiettyihin laskuihin.
Luokkien käytön periaate on, että luokan pitäisi muodostaa mahdollisimman itsenäinen kokonaisuus. Käyttöliittymä sisältää siis widgetit ja niiden toiminnot. Käyttöliittymästä tarvittaessa annetaan tietoa muualle ja luetaan tuloksia muualta. Muut osat ovat itsenäisiä niin, että täysin samaa Checking_payment-luokkaa pystyy käyttämään GUI-luokassa tai vaikka komentoriviohjelmassa. Eli GUI:ssa voi kirjoittaa self.näytä_tulos(cp.laske(self.hae_luku())), ja vastaavasti komentorivillä voisi kirjoittaa print(cp.laske(float(input()))).
Esimerkiksi kun tekstikentän sisältö pitää saada laskentaa varten toiselle luokalle ja tulos takaisin toiseen tekstikenttään (kuten juuri toivoit), tämä kannattaa tehdä kokonaan käyttöliittymän puolella:
class GUI: def luo_toiminnon_asiat(self): self.nappi = Nappi(command = lambda: self.aja_toiminto()) self.syöte = Tekstikenttä() self.tulos = Tekstikenttä() def aja_toiminto(self): s = self.syöte.hae_teksti() bruttopalkka = float(s) nettopalkka = laske_nettopalkka(bruttopalkka) t = "Nettopalkka = %.3f" % nettopalkka self.tulos.aseta_teksti(t)
Tässä siis käyttöliittymä sisältää widgetit (napit ja tekstikentät) ja kaiken niihin liittyvän koodin. Syöte myös muutetaan oikeaan muotoon (kuten luvuksi) jo käyttöliittymän puolella, ja vastauksen muotoilu oikeaan muotoon (kuten tekstiksi) tapahtuu käyttöliittymän puolella. Olen yksinkertaistanut asiaa niin, että laskentaa varten olisi vain funktio laske_nettopalkka, mutta tietenkin voit käyttää edelleen kokonaista luokkaa tähän asiaan. Siitä huolimatta kaikki syötteeseen ja tulosteeseen ja käyttöliittymään liittyvä tapahtuu GUI-luokassa, joten laskentaluokkaan (Checking_payment) jää vain pari kertolaskua ja ei mitään käyttöliittymään liittyvää koodia.
Myös käyttöliittymän voi jakaa useaan luokkaan. Esimerkiksi paneelista voi periyttää oman luokan, joka sitten sisältää tiettyyn asiaan liittyvät widgetit ja toiminnot. Luokan nimi voisi olla Checking_payment_GUI. Tällaisesta frameen perustuvasta luokasta oli esimerkki siinä aiemmassa keskustelussasi. Pääasiallisessa käyttöliittymässä voidaan sitten sopivaan kohtaan lisätä tämä valmis palikka nimeltä Checking_payment_GUI, aivan kuten lisättäisiin tyhjä frame. (Perinteisessä purkkaohjelmoinnissa voisi tuohon luokkaan laittaa myös asiaan liittyvän logiikan, kun logiikkaa on vain parin kertolaskun verran.)
Kannattaa tosin miettiä, että jos ohjelma on yksinkertainen ja koodin määrä vähäinen, niin onko erityistä järkeä lähteä pilkkomaan ohjelmaa useisiin luokkiin, tai ainakin että mitkä osat pilkkoo ja mitkä ei.
Esimerkiksi tuon aiemmin mainitun laskinesimerkkini olisi voinut ihan hyvin koodata kokonaan MainWindow-luokan sisälle ilman muita omia apuluokkia.
Koodasin myös laskimen kahdella eri tavalla, mistä näkee, miten paljon tavallaan ylimääräistä jargonia kertyy, jos haluaa tehdä asiat "ideologisesti oikein" mutta ehkäpä hieman epäkäytännöllisellä tavalla ongelman vaativuustason huomioiden.
Toisaalta jos tarkoitukseni olisi ollut jalostaa laskimesta monimutkaisempia laskutoimituksia hallitseva sovellus, niin tällöin B-versio olisi ollut jotakuinkin oikea lähestymistapa. Laskuja laskevan luokan laajentaminen ymmärtämään esimerkiksi sulkeita olisi suhteellisen helppoa, eikä sen tekemiseksi tarvitse oikeastaan koskea käyttöliittymäluokkiin. (Lisätään vain uudet napit Numpad-luokkaan ja ehkä kolmas signaali sulkeen syöttämistä ilmaisemaan.)
Mutta vaikka koko ohjelman kirjoittaisi yhden luokan sisälle, pätevät silti muut hyvät ohjelmointitavat. Eli yritetään pitää abstrakti laskenta erillään GUI-komponentteja käsittelevästä osasta koodia ja pilkotaan funktiot sopivan pieniksi kokonaisuuksiksi, jotka ratkaisevat yksinkertaisen osaongelman rajatulla joukolla parametreja, ja näitä apufunktioita voi sitten kutsua toisista funktioista. Ja niin edelleen.
Tervehdys,
kiistos vastauksesta.
>Esimerkiksi tuon aiemmin mainitun laskinesimerkkini olisi voinut ihan hyvin koodata kokonaan MainWindow-luokan sisälle ilman muita omia apuluokkia.>
Tätä tekniikkaa olen itsekin käyttänyt tähän saakka isommillekin ohjelmille eli koonnut kaikki funktiot käyttöliitymäluokan alle ja sellainen toimiva versio on tästäkin palkkionlaskuohjelmasta.
Jätetään asian käsittely tähän ja yritän opiskella luokkajakoa ja oop-ohjelmointia lisää ennen kuin lähden soveltamaan sitä käytännössä. On ilmeisesti tarpeenkin ...
Ongelmasi on siinä, että sanot ettet halua opetella luokkajakoa vaan jotain muuta, vaikka selvästi yrität opetella juuri luokkajakoa etkä mitään muuta. Ja sitten teet sen luokkajaon vituilleen ja ihmettelet, että mitenkäs nämä johdannaisongelmat nyt korjaisi. Ja joo, et osaa pythoniakaan kovin hyvin, kun sinulla on vaikeuksia ymmärtää, että miten luokkamuuttujat toimivat.
Sanoisin että sinun on tärkeintä oppia ensin koodaamaan ilman noita hulluja globaaleita muuttujia. Sen jälkeen voit opetella jakamaan koodia eri luokkiin. Toki tällaiset asiat voi opetella samalla kertaakin, jos tuntuu että oma kapasiteetti riittää siihen.
Ja jos teet sen virheen, että menet googlettamaan olio-ohjelmointia netistä ja löydät jonkin "oppaan", jossa kehotetaan käyttämään singleton patternia ja korvaamaan globaalit "kelluvat" muuttujat luokkiin sidotuilla globaaleilla muuttujilla, niin se ohjelmointitapa on sitten ihan yhtä kelvoton ja ihan yhtä perseestä. Älä lähde siihen.
Tervehdys,
kiitos palautteesta. Kopioin viimeisen palstan tekstin itselle kokoamiini ohjeisiin muistiin ja toivottavasti ostaan välttää sitä.
Myönnän kyllä, että on edetty vähän turhan nopeasti luomaan konkreettisia ohjelmia graafisella käyttöliittymällä ymärtämättä todella ohjelmoinnin peruskäsitteitä kunnolla ensin.
Mikä toi cross income oikein on? Liittyykö kirkollisveroon?
qeijohanseon kirjoitti:
Mikä toi cross income oikein on? Liittyykö kirkollisveroon?
Corss-income on Englannin kielinen termi ja sillä ratoitetaa bruttotuloa kun taas net-incomella nettotuloa.
walkout_ kirjoitti:
qeijohanseon kirjoitti:
Mikä toi cross income oikein on? Liittyykö kirkollisveroon?
Corss-income on Englannin kielinen termi ja sillä ratoitetaa bruttotuloa kun taas net-incomella nettotuloa.
Missasit vitsin, oikea englanninkielinen termihän on siis "gross income" tai "gross pay".
jalski kirjoitti:
(11.01.2021 10:05:40): ”– –” Missasit vitsin, oikea englanninkielinen...
Juu oikeassa Engallinssa ei ole yhtäkään yhdyssanaa mutta Ameerikassa nämme on ainakin kun käyttää minun firman USA-asiakkaiden palveluita. Jotka ilmoittaa minulle etukäteen % bruttopalkasta ja nettopalkasta - niiden välistä vetämä %.
Nimimerkillä mulla on Oxford Dictionary -himassa jossa selitty Englanniksi kaikki sen kieliset sanat USA:n Englannilla, Britti-Engalannilla ja Autraalian Engalnnilla erikseen.
Ja jos nyt tajuan permmin niin Cross Pay on buttopalkka ja Cross Income bruttoliikevaihto? ja sen tiedän jo että Pay Roll on vain palkkakierto eli milloin palkka maksetaan.
walkout_: cross = risti ja gross = brutto.
Aihe on lukittu, joten siihen ei voi vastata.