Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python-funktioilla liikaa parametreja

Jaska [05.02.2017 11:23:48]

#

Mulla on ongelma ohjelmien suunnittelussa. Tuntuu siltä, että aina kun teen vähänkään monimutkaista ohjelmaa, teen funktioista liian isoja ja ne tarvitsevat kamalan monta parametria. Miten kannattaisi opetella tekemään vähäparametrisia funktioita? Ainakin yksi tapa olisi pistää kaikki argumentit listaan ja funktio ottaisi parametrina tuon lista-argumentin. En usko, että tällainen olisi kovin tyylikästä.

fergusq [05.02.2017 11:48:37]

#

Vaikea sanoa tuntematta funktioitasi, mutta tässä pari ehdotusta:

Tee parametreista luokka

Jos parametrit liittyvät toisiinsa, kannattaa harkita niiden laittamista luokkaan. Tätä luokkaa kannattaa sitten käyttää muissakin funktioissa. Parhaimmillaan pääset samalla eroon koko joukosta muuttujia.

Alla lyhyt esimerkki.

# ennen
def tulostaTiedot(nimi, ikä, sukupuoli):
	print("Nimi:", nimi)
	print("Ikä:", ikä)
	print("Sukupuoli:", sukupuoli)

# jälkeen
class Henkilö:
	def __init__(self, nimi, ikä, sukupuoli):
		self.nimi = nimi
		self.ikä = ikä
		self.sukupuoli = sukupuoli

def tulostaTiedot(henkilö):
	print("Nimi:", henkilö.nimi)
	print("Ikä:", henkilö.ikä)
	print("Sukupuoli:", henkilö.sukupuoli)

Listaan parametrit kannattaa laittaa vain, jos ne oikeasti kuvaavat listaa (jonka koko voi vaihdella).

Kannattaa myös harkita funktioiden muuttamista metodeiksi. Esimerkissä tulostaTiedot voisi hyvin olla Henkilö-luokan metodi.

Jaa funktio osiin

Suuri määrä parametreja voi merkitä sitä, että funktiosi tekee liian monta asiaa. Ideaalisti yksi funktio tekee vain yhden asian.

Jos funktion voi helposti jakaa kahteen tai useampaan osaan, joista jokainen käyttää vain osaa argumenteista, kannattaa tehdä niin.

Tee parametreista globaaleja muuttujia

Jos funktiolle annetaan aina samat argumentit ohjelman suorituksen aikana, tai siis jos argumentit ovat vakioita, voisiko nämä laittaa globaaleihin muuttujiin?

# ennen
def tulostaLokiin(tiedosto, aika, viesti):
	# ...

# jälkeen
lokitiedosto = "log.txt"
def tulostaLokiin(viesti):
	tiedosto = lokitiedosto
	aika = datetime.datetime.now()
	# ...

Tyylikkäintä olisi tehdä tästäkin luokka Lokitiedosto, joka sisältää tiedoston nimen ja metodin tulostaLokiin. Silloin parametreja olisi yhtä vähän, mutta lokitiedostoja voisi silti tarvittaessa olla useampia.

Jaska [05.02.2017 19:24:54]

#

Yritin ratkoa putkapostia Ahdas ruudukko. Koodista tuli vaan ylimonimutkainen bugikasa.

The Alchemist [06.02.2017 21:37:06]

#

Vaikka luokkien käyttäminen on tavallaan yksi ratkaisu ongelmaan, niin se on aika usein kuitenkin aloittelijatason kysymyksissä huono ratkaisu, koska bloatti ja sekava luokka on aivan yhtä huono asia kuin liian monta parametria vaativa funktio.

Funktion tarkoitus on ratkaista ongelma. Iso ongelma tulee jakaa pienempiin osaongelmiin, jotka voi ratkaista itsenäisinä asioina tietyllä määrällä parametreja. Ratkaisut voi sen jälkeen ketjuttaa antamalla seuraavalle funktiolle edellisen tuottaman arvon yhtenä parametrina.

Aloittelija tekee usein myös sen virheen, että lähtee optimoimaan ratkaisua jo ennen kuin kyseistä ratkaisua on olemassa. Tällöin parametreihin saattaa tulla kaikenlaisia ylimääräisiä asioita, jotka olevinaan nopeuttavat laskentaa mutta samalla tekevät kaikesta niin hemmetin monimutkaista. Jos tästä on kyse, niin yritäpä ensin tehdä yksinkertaisin mahdollinen (ts. usein brute forceen perustuva) algoritmi ja mieti myöhemmin sen nopeuttamista, mikäli sille on edes tarvetta.

Kokelin testiksi ratkaista tuota Putkapostia tekemällä joukon yksinkertaisia funktioita ns. lennosta. Sain toimivan ratkaisun yhdeksällä funktiolla, joista jokainen ottaa enintään neljä parametria. Käytännössä loin aina uuden funktion, kun sisäkkäisiä silmukoita olisi tullut liikaa. (Ratkaisuni ei jostain syystä ole optimaalinen, koska löytää pienimmillään 3x8 ruudukon.)

P.S. Globaalit on syöpä, älä käytä niitä.

fergusq [06.02.2017 22:43:14]

#

The Alchemist kirjoitti:

P.S. Globaalit on syöpä, älä käytä niitä.

Globaalit ovat erinomainen ratkaisu tietyissä tilanteissa. Jos käytetyt arvot ovat vakioita koko ohjelman suorituksen ajan, ei ole mitään järkeä kuljettaa niitä parametreissa. Vaikka jokin käytetty algoritmi voisikin teoriassa käyttää muitakin vakion arvoja, on usein yksinkertaisten ohjelmien kannalta täysin turhaa tarjota niille tukea varsinkin, jos ohjelmaan sopivat vain tietyt vakion arvot.

Valmiiksi tunnetuille vakioille Python tarjoaakin jo oman ratkaisunsa: parametrien oletusarvot, mutta esimerkiksi tiedostosta ladattavat arvot voi laittaa globaaleihin muuttujiin. Lisäksi yllä antamassani esimerkissä lokitiedoston polku kannattaa pitää omassa muuttujassaan vaikkapa kooditiedoston alussa, jotta se on helppo löytää ja muuttaa tarvittaessa.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta