12 - animasjon og klasse
En "klasse" i bython er et objekt. Python er et objektorientert programmeringsspråk, som de fleste moderne programmerinsspråk er. Hittil har vi imidlertid kjørt programmer som har en begynnelse og en slutt.
Et objekt er noe som skapes, og er der så lenge programmet kjører. Et objekt kan ha egenskaper (variabler som inneholder tekst, tall, lister eller andre objekter) og funksjoner.
For å skjønne poenget med objekter er det greit å begynne med et enkelt eksempel. Vi kan for eksempel lage en robot, eller "bot" som det ofte kalles. Vi starter med å definere hva klassen (objekt-typen) skal hete ("Bot") og hva som skal skje når objektet initieres (__init__()
-funksjonen).
class Bot():
def __init__(self, name, greeting='Hello', presentation='My name is', feelings=False):
self.name=name
self.greeting=greeting
self.feelings=feelings
self.presentation=presentation
def answer(self,question):
if question=='Do you have feelings?':
return f'It is {self.feelings} that I have feelings'
if question=='What is your name?':
return f"{self.presentation} {self.name}"
if question=='Hi!':
return self.greeting
Vi har nå definert hva slags struktur boten vår skal ha. Da er det tid for å lage en bestemt utgave av 'boten, som vi kan kalle "Leif":
the_bot_leif = Bot('Leif', 'Hello!', 'My name is', True)
Om du har kjørt koden over, kan du nå teste ut 'boten:
the_bot_leif.answer('Hi!')
the_bot_leif.answer('What is your name?')
the_bot_leif.answer('Do you have feelings?')
I eksemplet over har vi laget én utgave av Bot, la oss lage en ny:
the_bot_steve = Bot('Steve', 'Good morning!', 'I am', False)
Vi kan nå hilse på denne utgaven av Bot:
the_bot_steve.answer('Hi!')
the_bot_steve.answer('What is your name?')
the_bot_steve.answer('Do you have feelings?')
Som vi ser er klasser gnerelle strukturer som vi kan kalle og brue gien med ulik initisiering. Klasser er kjernen av hva som kalles "objektorientert programmering", som betyr at det går an å programmere objekter og opererere på dem, i stedet for å kjøre et program fra A til Å.
Om trent alt du ser i et program er objekter av klasser. Denne tekstboksen er for eksempel et objekt, med et innhold som kan forandres, en plassering og et bestemt utseende.
Vi kan for eksempel bruke klasse til å laget et marked, med tilbud, etterspørsel og en likevekt. Denne klassen har en bestemt tilbudsfunksjon, etterspørselsfunksjon og du kan sette en skatt på markedet når du oppretter det.
Som du ser av __init__
-funksjonen, så regnes likevekspris og kvantum ut når markedet opprettes, med gitt skatt. Skatten legges til tilbudsfunksjonen, slik at når denne kalles, så vil det være inkludert skatt.
Til å regne ut likevekt, bruker vi sympy slik vi har lært i 5 - sympy.
import sympy as sp
class Market():
def __init__(self, tax=0):
self.tax = tax
x = sp.symbols('x')
eq_cond = sp.Eq(self.demand(x),self.supply(x))
x_eq = sp.solve(eq_cond,x)
self.x = float(x_eq[0])
self.p = self.demand(self.x)
def supply(self, x):
return (x**2)*(1/250)+10+self.tax
def demand(self, x):
return 3000/(100+x)
Vi kan ta en titt på figuren
from matplotlib import pyplot as plt
import numpy as np
m = Market()
fig, ax = plt.subplots()
x = np.linspace(1,80)
ax.plot(x, m.supply(x))
ax.plot(x, m.demand(x))
Ved å opprette to markeder, ett med skatt og ett uten, kan vi sammenligne situasjonen. Vi starter med å lage de to markedene, der vi setter en skatt på 10 i det ene markedet:
mkt = Market()
mkt_tax = Market(10)
Vi kan nå plotte disse. Vi plotter tilbud og etterspørsel i markedene på samme måte som før i 3 - matplotlib, bortsett fra at vi setter det inn i en funksjon.
Legg merke til at vi først lager et figur- og akseobjekt som vil være uendret, men at innholdet i akseobjektet slettes hver gang vi kaller save_plot
. Årsaken til dette er at vi lenger ned skal lage mange figurer. Om vi oppretter et nytt figurobjekt hver gang vil alle figurene trenge plass i minnet til PC'en, noe som vil gi unødvendig stor bruk av maskinens internminne.
%%capture
#%%capture avoids printing an empty figure here
import numpy as np
from matplotlib import pyplot as plt
#create figure and axis objects. These objects will be used for every plot, in order to conserve memory.
fig, ax=plt.subplots(figsize=(15,10), dpi=100)
def save_plot(market, market_taxed, i):
"""Plots market and market_taxed and saves to /img/fig<i>.png """
#clearing previouis plot. We use the same ax each time, and clear it before plotting, to save memory
ax.clear()
#formatting the plot:
ax.set_ylabel('Pris')
ax.set_xlabel('Enheter')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.set_xlim([0, 100])
ax.set_ylim([0, 40])
#setting the quantities to plot
x = np.linspace(0,100,100)
#drawing supply
ax.plot(x, market.supply(x), label='Tilbud')
#drawing supply after tax
ax.plot(x, market_taxed.supply(x), label='Tilbud etter skatt', color='black')
#drawing demand
ax.plot(x, market.demand(x), label='Etterspørsel', color='green',)
#adding legend:
ax.legend(loc='upper center',frameon=False)
#saving the plot:
fig.savefig(f'./img/fig{i}.png')
return fig
Vi kan nå plotte markedene, med og uten skatt:
save_plot(mkt, mkt_tax,0)
Om du nå sjekker i mappen 'img' i samme mappe som denne jupyter-filen ligger, vil du finne bildet over.
Vi skal nå lage en dynamisk nettside, med et skyvelær hvor vi kan velge skattenivå. Dette får vi til ved å bruke funksjonen save_plot
til å generere et sett med figurer, der hver figur representerer ett skattenivå. Ved å lage et skyvelær på en nettside kan man da få det til å se ut som om brukeren flytter kurven på nettsiden med skyvelæret.
Funksjonen create_html_file
under lager en html-fil i mappen der denne jupyter-filen ligger. Den tar rng
som argument, som representerer en etikett som skal vises til hvert bilde vi lager.
Funksjonen read_file
sørger for at create_html_file
kan lese to filer "res/html1.txt" og "res/html2.txt" som brukes til å lage html-filen.
Vi skal ikke gå veldig detaljert inn på hvordan funksjonen under fungerer. Det som er viktig for deg er å identifisere hva du må endre for å lage din egen side. Om du åpner "res/html1.txt" vil du finne en html-kode nederst som du kan redigere i, slik at du kan lage din egen interaktive nettside.
def read_file(fname):
with open(fname,'r') as f:
return f.read()
def create_html_file(rng):
"""This function creates a html-file with a slider that select the image for the desired year.
rng is an array with
It assumes that images with names /img/fig<i>.png have been saved"""
n=len(rng)
s=f"<br><br><input type='range' min='0' max='{n-1}' value='34' class='slider' id='myRange'><br><br>\r\n"
#iterating to create a html-tag for each image:
for k in range(len(rng)):
s+=f"<img class='slides' src='./img/fig{k}.png'>\r\n"
#building the html-code from a string and two files:
html=s+f"\r\n</body>\r\n<script>\r\n var array = JSON.parse('{list(rng)}');"
html_fileI = read_file("res/html1.txt")
html_fileII = read_file("res/html2.txt")
#creating the target file and writing to it, before closing:
with open("Tax and consumer surplus.html",'w') as result_file:
result_file=open("Tax and consumer surplus.html",'w')
result_file.write(html_fileI+html+html_fileII)
Vi kan nå lage nettsiden vår. Det gjør vi ved å lage 15 ulike skattenivåer i intervallet -30 til 18, og så lage ett plott for hvert nivå, med og uten skatt.
#creating the reference market
mkt=Market()
#defining dimensions for plot
fig, ax=plt.subplots(figsize=(15,10), dpi=100)
#creating a vector of tax-levels
taxes=np.linspace(-30,18,15)
#iteratign over tax levels.
i=0
for tax in taxes:
#creating a market with tax
mkt_taxed=Market(tax)
#saving the plot
save_plot(mkt,mkt_taxed,i)
i+=1
#creating the html-file
create_html_file(taxes)
Om du ser i img
-mappen, vi du se alle plottene der. Og i samme mappe som denne jupyter-filen vil du finne "Tax and consumer surplus.html". Klikker du på den, får du opp en dynamisk nettside som endrer på plott når du skyver på skyvelæret.
Vi kan gjøre figuren mer avansert ved å legge til flere elementer fra figuren i 5 - sympy. For markedet kan vi legge til en funksjon som regner ut konsument-, produsent- og velferdsgevinst. Disse legges så til som en egenskap ved markedet (en egenskap med self
) etter avrunding:
class Market():
def __init__(self, tax=0):
self.tax = tax
x = sp.symbols('x')
eq_cond = sp.Eq(self.demand(x),self.supply(x))
x_eq = sp.solve(eq_cond,x)
self.x = float(x_eq[0])
self.p = self.demand(self.x)
self.calc_welfare()
def supply(self, x):
return (x**2)*(1/250)+10+self.tax
def demand(self, x):
return 3000/(100+x)
def calc_welfare(self):
x=sp.symbols('x')
consumer_surplus = sp.integrate( self.demand(x) - self.p,
(x,0,self.x) )
producer_surplus = sp.integrate( self.p - self.supply(x),
(x,0,self.x) )
welfare_surplus = sp.integrate( self.demand(x) - mkt.supply(x),
(x,0,self.x) )
self.consumer_surplus = np.round(float(consumer_surplus),2)
self.producer_surplus = np.round(float(producer_surplus),2)
self.welfare_surplus = np.round(float(welfare_surplus),2)
def save_plot(market, market_taxed, i):
#clearing previouis plot
ax.clear()
#formatting the plot:
ax.set_ylabel('Pris')
ax.set_xlabel('Enheter')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.set_xlim([0, 100])
ax.set_ylim([0, 40])
#setting the quantities to plot
x = np.linspace(0,100,100)
#drawing supply
ax.plot(x, market.supply(x), label='Tilbud')
#drawing supply after tax
ax.plot(x, market_taxed.supply(x), label='Tilbud etter skatt', color='black')
#drawing demand
ax.plot(x, market.demand(x), label='Etterspørsel', color='green',)
#drawing consumer surpluss
x = np.linspace(0, market_taxed.x, 100)
ax.fill_between(x, float(market_taxed.p),
market_taxed.demand(x),
color = "pink",
alpha = 0.3,
label = f'Konsumentoverskudd: {market_taxed.consumer_surplus}')
#drawing producer surpluss
ax.fill_between(x, market_taxed.supply(x),
float(market_taxed.p),
color = "yellow",
alpha = 0.3,
label = f'Produsentoverskudd: {market_taxed.producer_surplus}')
ax.vlines(float(market_taxed.x), 0, 40,
colors=['black'],
linestyles='dashed',
label='Likevektskvantum')
#adding legend:
ax.legend(loc='upper center',frameon=False)
#saving the plot:
fig.savefig(f'./img/fig{i}.png')
return fig
Vi kan nå lage en ny nettside, med ekstrainformasjonen. Koden under er identisk med den vi kjørte sist:
#creating the reference market
mkt=Market()
#defining dimensions for plot
fig, ax=plt.subplots(figsize=(15,10), dpi=100)
#creating a vector of tax-levels
taxes=np.linspace(-30,18,15)
#iteratign over tax levels.
i=0
for tax in taxes:
#creating a market with tax
mkt_taxed=Market(tax)
#saving the plot
save_plot(mkt,mkt_taxed,i)
i+=1
#creating the html-file
create_html_file(taxes)
Nettsiden lages ved å hente teksten som ligger i filen "/res/html1.txt". Du skal åpne denne filen og rediger den slik at