Arcade-pelien ohjelmointi
Pythonilla ja Pygamella

Chapter 14: Kirjastot ja Modulit

fig.example_sprite_game

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öä:

  1. Koodi saadaan jaettua pienemppiin osiin ja ovat helpommin hallittavissa
  2. Samassa ohjelmaprojektissa voi työskennellä useita samanaikaisesti
  3. 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:

Video: Libraries

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

Video: Namespace

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.

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.

Alkuun tässä on koodi, joka luo yksinkertaisen viivadiagrammim neljällä arvolla:

fig.matplotlib_01
Figure 14.1: Simple Line Graph
"""
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.

fig.matplotlib_02
Figure 14.2: Specifying the x values
"""
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.

fig.matplotlib_03
Figure 14.3: Graphing two data series
"""
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:

fig.matplotlib_07
Figure 14.4: Adding a legend
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:

fig.matplotlib_10
Figure 14.5: Adding annotations
"""
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.

fig.matplotlib_04
Figure 14.6: Specifying the line style
"""
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.

fig.matplotlib_05
Figure 14.7: Bar chart
"""
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.

fig.matplotlib_06
Figure 14.8: X-axis labels
"""
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.

fig.matplotlib_08
Figure 14.9: Graphing a sine function
"""
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ä.

fig.matplotlib_09
Figure 14.10: Filling in a graph
"""
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.

fig.matplotlib_11
Figure 14.11: Pie chart
"""
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:

fig.matplotlib_12
Figure 14.12: Candlestick chart
"""
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.