Mikä on oikeaoppinen tapa tarkistaa, saako funktio parametrin? Harjoitustehtävänä on tehdä henkilötietojen haku- ja tulostusohjelma, ja ajattelin harjoitella funktioiden käyttöä. Mutta teenkö asian alla olevassa koodissa oikeaoppisesti vai en?
# -*- coding: utf-8 -*- def nimi(): nimimuuttuja = str(input("Anna nimi: ")) return nimimuuttuja def tulosta(nimi): tuloste="" if str(type(nimi)) != "<class 'function'>": nimi=str(nimi) tuloste+="Nimi: "+nimi print(tuloste) komento="" while komento!="q": komento=str(input("Anna komento:")) if komento=="n": nimi=nimi() if komento=="t": tulosta(nimi)
Koodissasi ei ole kyse siitä, saako funktio parametrin, vaan siitä, saako funktio parametriksi alussa määritellyn funktion vai myöhemmin syötetyn tekstin.
Mistä noin järjettömiä keksintöjä syntyy? On ihan älytöntä ensin määritellä funktio ja sitten luoda samanniminen tekstimuuttuja, ja vielä hullumpaa on joutua tarkistamaan tuota outoa tilannetta. Käytännön vika ohjelmassasi on, että jos syöttää n-komennon kahdesti, ohjelma kaatuu.
Järkevää olisi käyttää ensimmäiselle funktiolle eri nimeä ja alustaa muuttuja tyhjäksi. Silloin toisessa funktiossa voisi tarkistaa, onko parametri tyhjä, ja tilanne olisi paljon selvempi.
Ohjelma voisi toimia järkevästi vaikkapa näin:
def lue(): return input("Anna nimi: ") def tulosta(nimi): if nimi is None: print("Nimeä ei ole syötetty.") else: print("Nimi: " + nimi) nimi = None while True: komento = input("Anna komento: ") if komento == "q": break elif komento == "n": nimi = lue() elif komento == "t": tulosta(nimi) else: print("Tuntematon komento!")
Jaska kirjoitti:
Mikä on oikeaoppinen tapa tarkistaa, saako funktio parametrin?
Kun ohjelman suoritus menee funktion sisään, on sinne aina annettu parametri, jos funktion määrittely on tällainen:
def tulosta(nimi):
Tuo tarkoittaa, että funktio saa aina tasan yhden parametrin. Jos sitä yrittää kutsua eri määrällä, kutsun yhteydessä lentää poikkeus. Näitä voi testata helposti Python-komentokehotteessa tähän tapaan:
>>> def tulosta(nimi): ... pass ... >>> tulosta() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: tulosta() takes exactly 1 argument (0 given) >>>
Kun suoritus on edennyt tulosta-funktion sisään, nimi-muuttuja viittaa funktion sisällä aina sen saamaan parametriin eli funktion sisältä et voi helposti edes nähdä nimi-nimistä funktiota. Toki sekin onnistuu esim. globals-funktion käytöllä.
Tässä erilaisia keinoja määritellä funktiolle annettavia parametrejä. Näitä keinoja voi myös yhdistellä tiettyjen sääntöjen mukaan:
def tulosta(nimi): # yksi parametri def tulosta(nimi=None): # yksi vapaaehtoinen parametri, oletuksena None def tulosta(*nimet): # 0 tai useampi parametri, tulevat nimet-nimiseen tupleen def tulosta(**nimet): # 0 tai useampi nimetty parametri, tulevat nimet-nimiseen sanakirjaan
Esimerkki funktiosta ja sen kutsumisesta:
>>> def tulosta(sukunimi, etunimi=None, *osoitetiedot, **muut_tiedot): ... print sukunimi, etunimi, osoitetiedot, muut_tiedot ... >>> tulosta('Virtanen') Virtanen None () {} >>> tulosta('Virtanen', 'Matti') Virtanen Matti () {} >>> tulosta('Virtanen', 'Matti', 'Putkatie 1', '12345 Koodila') Virtanen Matti ('Putkatie 1', '12345 Koodila') {} >>> tulosta('Virtanen', 'Matti', 'Putkatie 1', '12345 Koodila', ovikoodi='01234') Virtanen Matti ('Putkatie 1', '12345 Koodila') {'ovikoodi': '01234'} >>> tulosta('Virtanen', ovikoodi='01234') Virtanen None () {'ovikoodi': '01234'}
Kiitos kummallekin! Sain nyt tehtyä tuon omasta mielestäni fiksummin, enkä huomannut enää kaatumisia.
# coding: utf-8 def lue_nimi(): return input("Anna nimi: ") def lue_ika(): kunnossa=False while not kunnossa: ika=input("Anna ikä: ") try: ika=int(ika) if ika>=0: kunnossa=True except ValueError: kunnossa=False return ika def lue_paikka(): return input("Anna asuinpaikka: ") def tulosta(nimi, ika, paikka): tuloste="" if nimi is not None: tuloste+="Nimi: "+nimi if ika is not None: if tuloste!="": tuloste+="\n" tuloste+="Ikä: "+str(ika) if paikka is not None: if tuloste!="": tuloste+="\n" tuloste+="Asuinpaikka: "+paikka print(tuloste) def ohje(): print("Ohje: n - syötä nimi, i - syötä ikä, a - syötä asuinpaikka, t - tulosta, o - ohje, q - lopeta") nimi=None ika=None paikka=None komento="" while komento!="q": komento=input("Anna komento: ") if komento=="n": nimi=lue_nimi() if komento=="i": ika=lue_ika() if komento=="a": paikka=lue_paikka() if komento=="t": tulosta(nimi,ika,paikka) if komento=="o": ohje()
Hyvä. Tyylillisesti tuossa olisi vielä parannettavaa, mutta perusharjoituksessa se ei ole niin olennaista. Tärkeintä on, että ohjelma toimii kuten pitää.
Näytän kuitenkin jatkoa varten miten tuo tulostaminen sujuu pythonmaisesti, listaa hyödyntäen.
def tulosta(nimi, ika, paikka): tuloste = [] if nimi is not None: tuloste.append('Nimi: ' + nimi) if ika is not None: tuloste.append('Ikä: ' + str(ika)) if paikka is not None: tuloste.append('Asuinpaikka: ' + paikka) print('\n'.join(tuloste))
Tai vielä tiiviimmin:
def tulosta(nimi, ika, paikka): tiedot = (('Nimi', nimi), ('Ikä', ika), ('Asuinpaikka', paikka)) tuloste = '\n'.join('%s: %s' % (s, x) for s, x in tiedot if x) print(tuloste)
Chiman kirjoitti:
Hyvä. Tyylillisesti tuossa olisi vielä parannettavaa, mutta perusharjoituksessa se ei ole niin olennaista.
Niin. Tyylejäkin on monenlaisia. Kurssilla sanottiin, että ohjelmakoodi tulisi alkaa rivillä
# -*- coding: utf-8 -*-
Toisaalta PEP8:n tyyliohjeessa tuota ei suositella. Mutta enpä olisi keksinyt googlata tuota tyyliohjetta ilman edellistä viestiäsi.
Jos tää on edelleen sitä samaa kurssia, jonka vetäjä tuntui olevan täysin pihalla pythonista, niin eipä tarvitse ihmetellä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.