Mae'r daflen lab yma yn cyflwyno rhaglennu gwrthrych-gyfeiriadol. Rydym wedi defnyddio rhaglennu gwerthrych-gyfeiriadol trwy gydol y taflennu lab. Fan hyn byddwn yn dysgu sut i greu gwrthrychau ein hunain (bydd yr hyn mae hwn yn golygu yn dod yn eglur cyn bo hir).
Gweithiwch trwy'r canlynol:
Fideo yn disgrifio'r cysyniad.
Y prif syniad tu ôl i raglennu gwrthrych-gyfeiriadol (OOP) yw creu strwythurau haniaethol felly nid oes angen poeni am ddata. Alan Kay daeth i fyny gyda'r cysyniad, a dyfynnwyd yn dweud: 'I wanted to get rid of data'. Yn lle cadw trac ar newidynnau trwy ddefnyddio rhestrau ac araeau ac ysgrifennu ffwythiannau penodol ar gyfer pob gweithrediad, gallwn geisio defnyddio system yn debyg i strwythur cell mewn bioleg:
Mae'r ddelwedd yma yn dangos amryw o bethau byddwn yn ystyried yn y daflen lab yma:
Mae creu dosbarth yn Python yn debyg i greu ffwythiant: rydym yn ysgrifennu lawr y rheolau:
In [1]:
class Myfyriwr:
"""Dosbarth sylfaenol"""
Yna gallwn greu 'achosion' o'r dosbarth yma:
In [2]:
vince = Myfyriwr()
zoe = Myfyriwr()
vince
Out[2]:
In [3]:
zoe
Out[3]:
Mae'r at ...
yn bwyntydd i leoliad yr achos yma yn gof y cyfrifiadur. Os ail-rhedwn y cod yna bydd y lleoliad yna yn newid.
Rydym ni wedi gweld enghreifftiau o ddosbarthiadau yn barod yn Python:
Mae llawer mwy, a byddwn yn gweld sut i adeiladu un ein hunain.
Arbrofwch gyda chreu achos o'r dosbarth Myfyriwr
Fideo yn disgrifio'r cysyniad.
Nid yw'r dosbarth uchod yn ddefnyddiol iawn. Nawr gwelwn sut i wneud i'n wrthrychau 'gafael' ar wybodaeth. Mae'r cod canlynol yn ail-greu ein dosbarth Myfyriwr blaenorol ond gan roi'r dosbarth rhai 'briodweddau':
In [4]:
class Myfyriwr:
cyrsiau = ['Bioleg', 'Mathemateg', 'Saesneg']
oedran = 12
rhyw = "Benyw"
Nawr mae gan y dosbarth ei hun bach o wybodaeth:
In [5]:
Myfyriwr.oedran
Out[5]:
Pasiwyd y wybodaeth yma i unrhyw achos o'r dosbarth:
In [6]:
vince = Myfyriwr()
vince.cyrsiau
Out[6]:
In [7]:
vince.oedran
Out[7]:
In [8]:
vince.rhyw
Out[8]:
Gallwn ddefnyddio ac/neu newid y priodweddau yma yn union fel unrhyw newidyn Python arall:
In [9]:
vince.oedran += 1
vince.oedran
Out[9]:
In [10]:
vince.rhyw = 'Gwryw'
vince.rhyw
Out[10]:
In [11]:
vince.cyrsiau.append('Cemeg')
vince.cyrsiau
Out[11]:
Crëwch achosion gyda phriodweddau ac arbrofwch gyda nhw.
Fideo yn disgrifio'r cysyniad.
Nawr byddwn yn gweld sut i wneud i ddosbarthiadau 'gwneud' pethau. Fe elwir y rhain yn 'dulliau' a jyst ffwythiannau ydynt sydd 'ynghlwm' a'r dosbarthau.
In [12]:
class Myfyriwr:
"""Dosbarth i gynrychioli myfyriwr"""
cyrsiau = ['Bioleg', 'Mathemateg', 'Saesneg']
oedran = 12
rhyw = "Female"
def cael_penblwydd(self, blynyddoedd=1):
"""Cynyddu'r oedran"""
self.oedran += blynyddoedd # Mae 'self' yn cyfateb a'r achos
vince = Myfyriwr()
vince.cael_penblwydd()
vince.oedran
Out[12]:
In [13]:
vince.cael_penblwydd(blynyddoedd=10)
vince.oedran
Out[13]:
Mae nifer o enwau dull 'arbennig' sy'n ymddwyn mewn ffordd benodol.
Un o rain yw __init__
, ac mae'r dull yma yn cael ei alw ar y foment crëir yr achos ('initialised'):
In [14]:
class Myfyriwr:
"""Dosbarth i gynrychioli myfyriwr"""
def __init__(self, cyrsiau, oedran, rhyw):
self.cyrsiau = cyrsiau
self.oedran = oedran
self.rhyw = rhyw
def cael_penblwydd(self, blynyddoedd=1):
"""Cynyddu'r oedran"""
self.oedran += blynyddoedd # Mae 'self' yn cyfateb a'r achos
Nawr mae'n hawdd creu achos gyda rhyw briodweddau penodol:
In [15]:
vince = Myfyriwr(["Maths"], 32, "Gwryw")
zoe = Myfyriwr(["Bioleg"], 31, "Benyw")
vince.cyrsiau, vince.oedran, vince.rhyw
Out[15]:
In [16]:
zoe.cyrsiau, zoe.oedran, zoe.rhyw
Out[16]:
Mae nifer o ddulliau 'arbennig' eraill, byddwn yn gweld un ohonynt yn yr enghraifft weithredol.
Crëwch achosion o'r dosbarth Myfyriwr gyda'r dulliau newydd yma.
Fideo yn disgrifio'r cysyniad.
Un agwedd olaf (ond bwysig iawn) o raglennu gwrthrych-gyfeiriadol yw'r cysyniad o etifeddiaeth. Mae hwn yn galluogi ni i greu dosbarthau newydd o rhai eraill. Yn ymarferol mae hwn yn arbed dyblygu cod oherwydd gallwn newid ond rhai dulliau fel sydd angen.
I wneud hwn rydym yn creu dosbarth newydd fel arfer ond trwy basio'r hen ddosbarth iddo:
class DosbarthNewydd(HenDdosbarth):
...
Er enghraifft, gadewch i ni greu myfyriwr sydd â phen-blwydd ar Chwefror 29 (dyddiad sydd ond yn digwydd unwaith pob 4 blwyddyn):
In [17]:
class MyfyriwrBlwyddynNaid(Myfyriwr):
"""Dosbarth ar gyfer myfyriwr a chafodd ei eni ar Chwefror 29"""
# Nodwch does dim angen ailysgrifennu'r dull init
def cael_penblwydd(self, blynyddoedd=1):
self.oedran += int(blynyddoedd / 4)
def cwyno(self):
"""Allbynnu string yn cwyno am ei phenblwydd"""
# MAe hwn yn dull newydd nad oes gan y dosbarth Myfyriwr
return "Dymunaf ni chefais fy ngeni ar Chwef 29"
Dyma sut mae'r dosbarth newydd yn ymddwyn:
In [18]:
geraint = MyfyriwrBlwyddynNaid(["Maths"], 22, "Gwryw")
geraint.cael_penblwydd()
geraint.oedran # Dal yn 22
Out[18]:
In [19]:
geraint.cael_penblwydd(8)
geraint.oedran
Out[19]:
In [20]:
geraint.cwyno()
Out[20]:
Arbrofwch gyda'r cod uchod: sut bydd yn gweithio os oedd blwyddyn nai pob 3 blwyddyn?
Fideo yn disgrifio'r cysyniad.
Tybiwch rydym ni eisiau astudio mynegiannau llinol. Mae'r mynegiannau ar ffurf:
$$ ax+b $$Mae gennym ddiddordeb, er enghraifft, yn y gwerth $x$ lle mae'r mynegiant llinol yn 0. Fe elwir hwn yn 'gwraidd' ac mae'n ddatrysiad i'r hafaliad canlynol:
$$ ax+b=0 $$Yn amlwg mae hwn yn beth hawdd i'w astudio ond tybiwn fod e ddim, ac adeiladwn ddosbarth i gynrychioli a thrin mynegiannau llinol.
In [21]:
class MynegiantLlinol:
"""Dosbarth ar gyfer mynegiant llinol"""
def __init__(self, a, b):
self.a = a
self.b = b
def gwraidd(self):
"""Allbynnu gwraidd y mynegiant llinol"""
return - self.b / self.a
def __add__(self, mynegiant):
"""Dull arbennig: yn galluogi ni i adio mynegiannau"""
return MynegiantLlinol(self.a + mynegiant.a, self.b + mynegiant.b)
def __repr__(self):
"""Dull arbennig: yn newid sut arddangosir yr achos"""
return "Mynegiant llinol: " + str(self.a) + "x + " + str(self.b)
In [22]:
myn = MynegiantLlinol(2, 4)
myn # rhoddir yr allwbn yma gan y dull '__repr__'
Out[22]:
In [23]:
myn.a
Out[23]:
In [24]:
myn.b
Out[24]:
In [25]:
myn.gwraidd()
Out[25]:
In [26]:
myn2 = MynegiantLlinol(5, -2)
myn2
Out[26]:
In [27]:
myn + myn2 # Mae hwn yn gweithio oherwydd y dull `__add__`
Out[27]:
Mae'r dosbarth yma yn gweithio'r iawn, ond rydym yn gweld problem yn eithaf cloi:
In [28]:
myn1 = MynegiantLlinol(2, 4)
myn2 = MynegiantLlinol(-2, 4)
myn3 = myn1 + myn2
myn3
Out[28]:
In [29]:
myn3.gwraidd()
Cawn wall gan fod y dull gwraidd
yn ceisio rhannu gyda sero.
Cywirwn ni hwn:
In [30]:
class MynegiantLlinol:
"""Dosbarth ar gyfer mynegiant llinol"""
def __init__(self, a, b):
self.a = a
self.b = b
def gwraidd(self):
"""Allbynnu gwraidd y mynegiant llinol"""
if self.a != 0:
return - self.b / self.a
return False
def __add__(self, mynegiant):
"""Dull arbennig: yn galluogi ni i adio mynegiannau"""
return MynegiantLlinol(self.a + mynegiant.a, self.b + mynegiant.b)
def __repr__(self):
"""Dull arbennig: yn newid sut arddangosir yr achos"""
return "Mynegiant llinol: " + str(self.a) + "x + " + str(self.b)
In [31]:
myn3 = MynegiantLlinol(0, 8)
myn3.gwraidd()
Out[31]:
Defnyddiwn ni hwn i wirio'r faith syml canlynol. Os yw $f(x) = a_1x+b_1$ ac $g(x) = a_2x+b_2$, yna rhoddir gwraidd $f(x) + g(x)$ gan:
$$ \frac{a_1x_1 + a_2x_2}{a_1+a_2} $$Lle $x_1$ yw gwraidd $f$ ac $x_2$ yw gwraidd $g$ (os ydynt yn bodoli).
Yn gyntaf ysgrifennwn ffwythiant sy'n gwirio hwn ar gyfer rhyw set o $a_1, a_2, b_1, b_2$.
In [32]:
def gwirio_canlyniad(a1, a2, b1, b2):
"""Gwirio bod y canlyniad wedi bodloni"""
f = MynegiantLlinol(a1, b1)
g = MynegiantLlinol(a2, b2)
k = f + g
x1 = f.gwraidd()
x2 = g.gwraidd()
x3 = k.gwraidd()
if (x1 is not False) and (x2 is not False) and (x3 is not False):
# Yn tybio bod gennym tair mynegiant ar gyfer y gwraidd
return (a1 * x1 + a2 * x2) / (a1 + a2) == x3
return True # Os nad oes gan f, g gwreiddiau mae'r perthynas dal yn wir
gwirio_canlyniad(2, 3, 4, 5)
Out[32]:
Byddwn yn gwirio hwn trwy samplu hap-werthoedd ar gyfer $a_1, a_2, b_1, b_2$.
In [33]:
import random # Mewnforio'r modiwl random
N = 1000 # Nifer o samplau
gwiriadau = []
for _ in range(N):
a1 = random.randint(-10, 10)
a2 = random.randint(-10, 10)
b1 = random.randint(-10, 10)
b2 = random.randint(-10, 10)
gwiriadau.append(gwirio_canlyniad(a1, a2, b1, b2))
all(gwiriadau)
Out[33]:
Ymarfer datbygio
Mae'r canlynol yn gais i ysgrifennu dosbarth ar gyfer petryal, canfyddwch a chywirwch yr holl fygs.
class Petryal:
"""Dosbarth ar gyfer petryal""
def __init__(lled, hyd)
self.lled = lled
self.hyd = lled
def cael_arwynebedd(self:
"""Cael arwynebedd y petryal"""
return self.lled * self.hyd
def yn_sgwar():
"""Gwirio os yw'r petryal yn sgwar"""
return self.lled == self.hyd
In [34]:
class Petryal:
# """Dosbarth ar gyfer petryal"" Nifer anghywir o "
"""Dosbarth ar gyfer petryal"""
#def __init__(lled, hyd) Does dim self nac :
def __init__(self, lled, hyd):
self.lled = lled
#self.hyd = lled # Aseiniad anghywir
self.hyd = hyd
#def cael_arwynebedd(self: Cromfach ar goll
def cael_arwynebedd(self):
"""Cael arwynebedd y petryal"""
return self.lled * self.hyd
#def yn_sgwar(): Does dim self
def yn_sgwar(self):
"""Gwirio os yw'r petryal yn sgwar"""
return self.lled == self.hyd
Creu petryal nad yw'n sgwâr:
In [35]:
petryal = Petryal(5, 4)
petryal.cael_arwynebedd(), petryal.yn_sgwar()
Out[35]:
Creu petryal sgwâr:
In [36]:
sgwar = Petryal(3, 3)
sgwar.cael_arwynebedd(), sgwar.yn_sgwar()
Out[36]:
In [37]:
import math
class MynegiantCwadratig:
"""Dosbarth ar gyfer mynegiant cwadratig"""
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def gwraidd(self):
"""Allbynnu gwreiddiau'r mynegiant cwadratig"""
gwahanolyn = self.b ** 2 - 4 * self.a * self.c
if gwahanolyn >= 0:
x1 = - (self.b + math.sqrt(gwahanolyn))/ (2 * self.a)
x2 = - (self.b - math.sqrt(gwahanolyn))/ (2 * self.a)
return x1, x2
return False
def __add__(self, myncwad):
"""Dull arbennig sy'n galluogi adio mynegiannau"""
return MynegiantCwadratig(self.a + myncwad.a, self.b + myncwad.b, self.c + myncwad.c)
def __repr__(self):
"""Dull arbennig: yn newid sut arddangosir yr achos"""
return "Mynegiant cwadratig: " + str(self.a) + "x ^ 2 + " + str(self.b) + "x + " + str(self.c)
In [38]:
cwad = MynegiantCwadratig(1, 5, 2)
cwad
Out[38]:
In [39]:
cwad.gwraidd()
Out[39]:
In [40]:
cwad2 = MynegiantCwadratig(4, 5, 2)
cwad + cwad2
Out[40]:
Os yw diferion glaw yn cwympo ar hap ar sgwâr gyda hyd $2r$ yna rhoddir tebygolrwydd o'r diferion yn glanio mewn cylch gyda radiws $r$ gan:
$$ P = \frac{\text{Arwynebedd cylch}}{\text{Arwynebedd sgwâr}}=\frac{\pi r ^2}{4r^2}=\frac{\pi}{4} $$Felly gallwn amcangyfrif $P$ ac felly amcangyfrif $\pi$ fel $4P$. Yn y cwestiwn yma byddwn yn ysgrifennu cod i amcangyfrif $P$ trwy ddefnyddio'r llyfrgell random.
Yn gyntaf, crëwn ddosbarth ar gyfer diferyn o law (sicrhewch eich bod yn deall y cod yma!):
class Diferyn():
def __init__(self, r=1):
self.x = (.5 - random.random()) * 2 * r
self.y = (.5 - random.random()) * 2 * r
self.mewncylch = (self.y) ** 2 + (self.x) ** 2 <= (r) ** 2
Nodwch fod y cod uchod yn defnyddio'r hafaliad yma ar gyfer cylch gyda chanol yn $(0,0)$ a radiws $r$:
$$ x^2+y^2≤r^2 $$I amcangyfrif $P$ crëwn $N=1000$ achos o Diferyn a chyfri'r nifer o rheini sydd o fewn y cylch. Defnyddiwch hwn i amcangyfrif $\pi$.
(Mae hwn yn enghraifft o dechneg a elwir yn efelychiad Monte Carlo.)
In [41]:
import random
import math # Nid yw hwn yn angenrheidiol ond byddwn yn ei defnyddio ar gyfer cymhariaethau nes ymlaen.
class Diferyn():
"""
Dosbarth ar gyfer diferyn o law ar leoliad ar hap ar sgwar
"""
def __init__(self, r=1):
self.x = (.5 - random.random()) * 2 * r
self.y = (.5 - random.random()) * 2 * r
self.mewncylch = (self.y) ** 2 + (self.x) ** 2 <= (r) ** 2
# Mae hwn yn allbynnu boolean yn cyfateb gyda os yw'r pwynt o fewn y cylch neu peidio
def amcangyfrifpi(N=1000):
"""
Ffwythiant sy'n allbynnu amcangyfrif ar gyfer pi gan ddefnyddio efelychiad monte carlo
"""
niferpwyntiaumewncylch = 0
for i in range(N): # Lwp ar gyfer nifer i diferion
diferyn = Diferyn() # Generadu diferyn newydd
if diferyn.mewncylch: # Gwirio os yw o fewn y cylch
niferpwyntiaumewncylch += 1
return 4 * niferpwyntiaumewncylch / float(N)
In [42]:
amcangyfrifpi()
Out[42]:
In [43]:
class Pwynt():
"""
Dosbarth ar gyfer pwynt sy'n disgyn yn leoliad ar hap o fewn sgwar
Priodweddau:
x: y gyfesuryn 'x' y pwynt
y: y gyfesuryn 'y' y pwynt
odangraff: boolean yn nodi os yw'r pwynt o dan y graph neu peidio
"""
def __init__(self, r=1):
self.x = random.random()
self.y = random.random()
self.odangraff = 1 - (self.x) ** 2 >= self.y
# Mae hwn yn allbynnu boolean sy'n gwirio os yw'r pwynt o dan y graff neu beidio
def amcangyfrifint(N=1000):
"""
Ffwythiant sy'n allbynnu amcangyfrif yr integryn gan ddefnyddio efelychiad monte carlo
Dadleuon: N (diofyn=1000) sef nifer o bwyntiau
Allbynnau: Amcangyfrif yr integryn
"""
niferodanygraff = 0
for i in range(N): # Lwp i ostwng nifer o bwyntiau
pwynt = Pwynt() # Generadu pwynt newydd
if pwynt.odangraff: # Gwirio os yw o dan y graff
niferodanygraff += 1
return niferodanygraff / float(N)
In [44]:
amcangyfrifint()
Out[44]:
In [ ]: