Arcade-pelien ohjelmointi
Pythonilla ja PygamellaChapter 14: Kirjastot ja Modulit
Kirjasto (library) on kokoelma funktioita ja luokkia. Useasti kirjastot ohjelmoidaan yleiskäyttöisiksi, toisissa ohjelmissa käytettäviksi, jolloin koodia (kirjastoa) voidaan hyödyntää useissa eri ohjelmissa. Eli ohjelmoijan ei tarvitse “keksiä pyörää uudelleen.” Pythonissa kirjastoja kutsutaan moduleiksi.
Niissä kohdissa koodia, joissa kirjoitettiin import pygame ja import random, ohjelmissa käytettiin jo moduleja. Kirjasto voidaan toteuttaa useasta eri modulista, jotka voidaan importata ohjelmaan.
Modulit järjestetään usein toiminnallisuuden mukaisesti ryhmiin. Tämän kurssin ohjelmissa olemme käyttäneet jo esimerkiksi math modulia, random modulia ja pygame kirjastoa. Moduli voi sisältää useitamuita moduleja. Esimerkiksi, pygame moduli sisältää alimodulit pygame.draw, pygame.image, ja pygame.mouse.
Moduleja ei ladata ellei ohjelma kutsu niiden latausta. Tämä säästää suoritusaikaa ja muistia. Tässä kappalseesa opitaan luomaan moduli ja miten se importataan ohjelmaan.
14.1 Miksi kirjastoja?
Kirjaston käyttämiseen on kolme seikkaa, jotka puoltavat niiden käyttöä:
- Koodi saadaan jaettua pienemppiin osiin ja ovat helpommin hallittavissa
- Samassa ohjelmaprojektissa voi työskennellä useita samanaikaisesti
- Kirjoitettu koodi on helppo jakaa muille ohjelmoijille.
Jotkut aiemmin tässä materiaaleissa esitetyistä ohjelmista olivatkin jo melko pitkiä. Jakamalla rivimäärältään pitkät ohjelmat useampaan eri ohjelmaan, voidaan koodia hallita helpommin. Esimerkiksi, edellä esitetyssä sprite-esimerkissä, sprite-luokka voitaisiin siirtää kokonaan erilliseen tiedostoon. Monimutkaisemmassa ohjelmassa jokainen sprite voitaisiin esittää omassa tiedostossaan.
Jos samaa projektia koodaa udeampi koodaaja, on lähes mahdotonta, että he ohjelmoisivat samaa tiedostoa. Jakamalla ohjelma useampaan osaan, saadaan samanaikainen koodaus mahdolliseksi. Yksi koodaja voisi kehittää esimerkiksi “Orc” sprite-luokkaa. Toinen koodaisi vaikkapa “Goblin” sprite-luokkaa. Erillisten tiedostojen koodaaminen ei aiheuta ristiriitoja ohjelman suunnittelussa.
Ammatikseen ohjelmoivat harvoin tekevät ohjelmia 'tyhjästä' koodaamalla rivi kerrallaan. Ohjelma on jaettu toiminnallisiin osiin, samantyyppinen toiminta samaan tiedostoon. Samat toiminnallisuudet voidaan koota kirjastoon ja jos jokin ohjelman osa tarvitsee kirjaston tarjoamia ominaisuuksia, voidaan niitä kutsua sieltä.
14.2 Miten luot oman Modulin/Kirjastotiedoston:
Tässä esimerkissä jaetaan ohjelma useaan pieneen osaan tiedostoiksi. Tässä tiedostossa test.py on yksi funktio, jota kutsutaan:
# Foo function def foo(): print("foo!") # Foo call foo()
Todellakaan, tässä tiedosto ei ole kovin pitkä, mutta jos sekä pääohjelma että funtio olisivat pitkiä, tilanne olisi toinen. Esimerkiksi 100 riviä pitkiä, useita funktioita sisältävä tiedosto olisi hankalasti ylläpidettävissä. Pidetään tässä esimerkissä tiedosto lyhyenä selvyyden vuoksi.
Voimme siirtää foo funktion tiedoston ulkopuolelle ja tähän tiedostoon jäisi ainoastaan pääohjelmakoodi. (Tässä esimerkissä osiin jakaminen tehdään ainoastaan oppimisen kannalta. )
Aloita kopioimalla foo funktio uuteen tiedostoon. Tallenna uusi tiedosto nimellä my_functions.py. Tiedoston pitää olla samassa hakemistossa/kansiossa test.py.
# Foo function def foo(): print("foo!")
# Foo funktion kutsu ei toimi foo()
Valitettavasti tämä ei ole ihan näin yksinkertaista. test.py ei 'osaa' hakea funktiota tiedostosta my_functions.py. Tässä pitää olla import komento:
# Import the my_functions.py file import my_functions # Foo funktion kutsu ei toimi vieläkään foo()
Tämä ei toimi vieläkään. Mikä vikana? Samalla tapaa kuin importtasimme pygamen yhteydessä, pitää funktion nimen eteen laittaa tiedostopaketin nimi:
# Import the my_functions.py file import my_functions # Foo funktion kutsu toimii my_functions.foo()
Tämä vihdoin toimii, koska my_functions. on pistenotaatiolla mukana funktion kutsussa.
14.3 Nimiavaruus
Ohjelmassa voisi olla kaksikin kirjastotiedostoa käytössä. Mitä tapahtuu, jos kirjastoissa on samannimisiä funktioita. Jos, esimerkiksi olisi kaksi funktiota nimeltään print_report, toinen tulostaisi arvosanat ja toinen printtaisi tilitietoja? Esimerkiksi:
def print_report(): print("Opiskelijan arvosanaraportti:" )
def print_report(): print("Tilitiedot:" )
Miten ohjelmassa saadaan määritettyä, mitä funktiota milloinkin kutsutaan? Tämä onnistuu melko helposti käyttämällä nimiavaruutta. Nimiavaruus tarkoittaa käytännössä kirjaton nimen kirjoittamista funktion nimen eteen. Katso koodiesimerkki alla:
import student_functions import financial_functions student_functions.print_report() financial_functions.print_report()
Esimerkissä näemme, miksi nimiavaruus on pakollinen. Jos ei käytettäisikään samoja nimiä? Nimiavaruuden kirjoittaminen uudelleen ja uudelleen voi alkaa tuntua turhauttavalta. Voit kiertää tämän käyttämällä paikallista nimiavaruutta. Paikallinen nimiavaruus on lista funktioista, muuttujista ja luokista, eikä niitä tarvitse kirjoittaa pistenotaatiolla nimiavaruuden alkuun. Palataan takaisin foo esimerkkiin. Poistetaan alkuperäinen import ja korvataan se uudella lauseella:
# import foo from my_functions import * foo()
Tämä toimii ilman my_functions. pistenotaatiota funktion kutsussa. Asteriski on villimerkki, jolla saadaan importattua kaikki my_functions kirjaston funktiot ohjelmaan. Kirjastosta voidaan importata funktioita yksittäin kirjoittamalla tähtimerkin paikalle funktion nimi.
14.4 Kolmannen osapuolen kirjastot
Pythonissa on tarjolla useita valmiiksi 'rakennettuja' aliohjelmakirjastoja.
Tästä voit katsoa kaikki tarjolla olevat valmiit kirjastot:
http://docs.python.org/3/py-modindex.html
Voit halutessasi ladata kaikki kirjastot. Niitä on webiin, kompleksilukuihin, tietokantoihin ja moneen muuhun tarkoitukseen.
- Pygame: Kirjastoa käytetään pelien luomiseen.
http://www.pygame.org/docs/ - wxPython: Graafisen käyttöliittymän luomiseen (GUI), ikkunat, valikot jne.
http://www.wxpython.org/ - pydot: Generoi komleksista suoraa ja epäsuoraa grafiikkaa
http://code.google.com/p/pydot/ - NumPy: Monimutkaisten matriisien käsittelyyn.
http://numpy.scipy.org/
Erinomainen lista Pythonin kirjastoista ja asennuslinkeistä löytyy tästä:
http://www.lfd.uci.edu/~gohlke/pythonlibs/
Selaamalla läpi saatavilla olevia ohjelmakirjastoja saat hyvän yleiskuvan erilaisista mahdollisuuksista. Ohjelmien tekeminen on paljon nopeampaa käyttämällä valmiita 'osia' kuin kirjoittaa kaikki koodit itse.
14.4.1 Esimerkki: Matplotlib kirjasto
Tässä on esimerkki siitä, mitä voit tehdä 'kolmannen osapuolen' kirjastolla “Matplotlib.” Jos olet kiinnostunut vain pelien tekemisestä, voit ohittaa tämän kohdan. Toivottavaa tietysti olisi, että kävisit esimerkin läpi, jolloin saisit hyvän kuvan kirjastojen käyttömahdollisuuksista.
Matplotlib käyttää monia muita kirjastoja, joten sinun on asennettava kaikki alla olevat osat. Osat ovat pieniä ja lataantuvat nopeasti.
- matplotlib-1.4.2.win32-py3.3.exe
- six-1.8.0.win32-py3.3.exe
- numpy-MKL-1.9.1.win32-py3.3.exe
- pyparsing-2.0.3.win32-py3.3.exe
- python-dateutil-2.2.win32-py3.3.exe
Alkuun tässä on koodi, joka luo yksinkertaisen viivadiagrammim neljällä arvolla:
""" Line chart with four values. The x-axis defaults to start at zero. """ import matplotlib.pyplot as plt y = [1, 3, 8, 4] plt.plot(y) plt.ylabel('Element Value') plt.xlabel('Element Number') plt.show()
Huomaa, että voit zoomata, panoroida ja tallentaa kuvaajan. Tallennus onnistuu jopa vektorikuvana kuten ps ja svg tyyppisenä, jolloin kuvan laatu on parempi, kuten rasterigrafiikassa.
x:n arvo Esimerkissä 1, Kuvassa 14.1 alkaa oletuksena nollasta. Tätä oletusarvioa voidaan muuttaa antamalla arvot x:lle ja y:lle. Katso esimerkki 2 alla.
""" Line chart with four values. The x-axis values are specified as well. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.plot(x, y) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Uuden arvosarjan lisääminen graafiin olisi varsin yksinkertaista.
""" This example shows graphing two different series on the same graph. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] plt.plot(x, y1) plt.plot(x, y2) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Voit lisätä kuvaajaan myös selitteen:
import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] plt.plot(x, y1, label = "Series 1") plt.plot(x, y2, label = "Series 2") legend = plt.legend(loc='upper center', shadow=True, fontsize='x-large') legend.get_frame().set_facecolor('#00FFCC') plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Voit lisätä kuvaajaan huomautuksia:
""" Annotating a graph """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.annotate('Here', xy = (2, 3), xycoords = 'data', xytext = (-40, 20), textcoords = 'offset points', arrowprops = dict(arrowstyle="->", connectionstyle="arc,angleA=0,armA=30,rad=10"), ) plt.plot(x, y) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Kuvaajan viivan tyyliä voidaan vaihtaa lisäämällä plot komentoon kolmas parametri.
""" This shows how to set line style and markers. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] # First character: Line style # One of '-', '--', '-.', ':', 'None', ' ', ” # Second character: color # http://matplotlib.org/1.4.2/api/colors_api.html # Third character: marker shape # http://matplotlib.org/1.4.2/api/markers_api.html plt.plot(x, y1, '-ro') plt.plot(x, y2, '--g^') plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Viivakaavio voidaan helposti muuttaa pylväskaavioksi.
""" How to do a bar chart. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.bar(x, y) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Akseleille voidaan lisätä selitteet.
""" How to add x axis value labels. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.plot(x, y) labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs'] plt.xticks(x, labels) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Voit piirtää myös funktioiden kuvaajia. Tähän tarvitaan toinen kirjastopaketti nimeltään numpy, jolla saadaan esim. sini-funktion kuvaaja.
""" Using the numpy package to graph a function over a range of values. """ import numpy import matplotlib.pyplot as plt x = numpy.arange(0.0, 2.0, 0.001) y = numpy.sin(2 * numpy.pi * x) plt.plot(x, y) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Voit täyttää kuvaajan täyttövärillä.
""" Using 'fill' to fill in a graph """ import numpy import matplotlib.pyplot as plt x = numpy.arange(0.0, 2.0, 0.001) y = numpy.sin(2 * numpy.pi * x) plt.plot(x, y) # 'b' means blue. 'alpha' is the transparency. plt.fill(x, y, 'b', alpha=0.3) plt.ylabel('Element Value') plt.xlabel('Element') plt.show()
Luodaan seuraavaksi ympyräkaavio.
""" Create a pie chart """ import matplotlib.pyplot as plt # Labels for the pie chart labels = ['C', 'Java', 'Objective-C', 'C++', 'C#', 'PHP', 'Python'] # Sizes for each label. We use this to make a percent sizes = [17, 14, 9, 6, 5, 3, 2.5] # For list of colors, see: # https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/colors.py colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral', 'darkcyan', 'darksage', 'rosybrown'] # How far out to pull a slice. Normally zero. explode = (0, 0.0, 0, 0, 0, 0, 0.2) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') # Finally, plot the chart plt.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90) plt.show()
Erilaisten kuvaajien luomisessa vain mielikuvitus on rajana. Esimerkiksi webistä poimitusta datasta saadaan tehtyä 'kynttilä'kaavio:
""" Create a candlestick chart for a stock """ import matplotlib.pyplot as plt from matplotlib.dates import DateFormatter, WeekdayLocator,\ DayLocator, MONDAY from matplotlib.finance import quotes_historical_yahoo_ohlc, candlestick_ohlc # Grab the stock data between these dates date1 = (2014, 10, 13) date2 = (2014, 11, 13) # Go to the web and pull the stock info quotes = quotes_historical_yahoo_ohlc('AAPL', date1, date2) if len(quotes) == 0: raise SystemExit # Set up the graph fig, ax = plt.subplots() fig.subplots_adjust(bottom=0.2) # Major ticks on Mondays mondays = WeekdayLocator(MONDAY) ax.xaxis.set_major_locator(mondays) # Minor ticks on all days alldays = DayLocator() ax.xaxis.set_minor_locator(alldays) # Format the days weekFormatter = DateFormatter('%b %d') # e.g., Jan 12 ax.xaxis.set_major_formatter(weekFormatter) ax.xaxis_date() candlestick_ohlc(ax, quotes, width=0.6) ax.autoscale_view() plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right') plt.show()
matplotlib kirjaston avulla voit luodä monia erilaisia kaavioita ja kuvaajia. Katso seuraavia kuvakkeita:
http://matplotlib.org/gallery.html
14.5 Review
14.5.1 Multiple Choice Quiz
Klikkaa tästä monivalintatehtävään.
14.5.2 Short Answer Worksheet
Klikkaa tästä kappaleen kertaustehtäviin.
14.5.3 Lab
Klikkaa tästä ohjelmointitehtäviin.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin