After init metaluokan avulla
Tämä on melko huikea temppu, eikä tätä tarvitse ymmärtää ja harvoin tarvitsee käytää. Käytämme Pythonin metaluokkia ja niiden avulla
pystymme muuttamaan yläluokan luontimetodin toimintaa niin, että se kaikkien sisäkkäisten alaluokkien luontimetodien kutsujen jälkeen kutsuu
metodia __after__init__
.
# Kuviot kehiin
from math import pi
class AfterInitWrapper(type):
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
obj.__after_init__()
return obj
class Shape(object, metaclass=AfterInitWrapper):
def __init__(self, color):
self.color = color
def __after_init__(self):
self.area = self.compute_area()
def samecolor(self, other):
return self.color == other.color
def info(self):
print(str(self))
def compute_area(self):
pass
def intesects(self, othe):
pass
class Ellipse(Shape):
def __init__(self, center, a, b, color):
super().__init__(color)
self.center = center
self.a = a
self.b = b
def __str__(self):
return '{}(center={}, a={}, b={}, color={})'.format(type(self).__name__, self.center, self.a, self.b, self.color)
def compute_area(self):
return pi * self.a * self.b
class Circle(Ellipse):
def __init__(self, center, radius, color):
super().__init__(center, radius, radius, color)
self.radius = radius
def __str__(self):
return '{}(center={}, radius={}, color={})'.format(type(self).__name__, self.center, self.radius, self.color)
class Rectangle(Shape):
def __init__(self, point1, point2, color):
super().__init__(color)
self.point1 = point1
self.point2 = point2
(x1, y1) = point1
(x2, y2) = point2
self.width = abs(x1 - x2)
self.height = abs(y1 - y2)
def __str__(self):
return '{}(point1={}, point2={}, color={})'.format(type(self).__name__, self.point1, self.point2, self.color)
def compute_area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, center, side, color):
(x, y) = center
super().__init__((x - side / 2, y - side / 2), (x + side / 2, y + side / 2), color)
self.center = center
self.side = side
def __str__(self):
return '{}(center={}, side={}, color={})'.format(type(self).__name__, self.center, self.side, self.color)
crimson = (0xDC, 0x14, 0x3C)
white = (0xFF, 0xFF, 0xFF)
indigo = (0x4B, 0x00, 0x82)
shapes = [Circle((10.0, 20.0), 2.5, crimson),
Rectangle((-5.0, 5.0), (5.0, 5.0), white),
Ellipse((0.0, -5.0), 7.5, 6.0, indigo),
Circle((10.5, 19.5), 3.0, crimson),
Square((5.0, -5.0), 8.5, white),
Ellipse((6.0, 10.0), 7.5, 6.0, indigo),
Square((20.0, 30.0), 10.0, indigo)]
for s in shapes:
s.info()
for i in range(len(shapes)):
for j in range(i + 1, len(shapes)):
print('Muodot {} ja {} ovat {}väriset'.format(shapes[i], shapes[j], 'saman' if shapes[i].samecolor(shapes[j]) else 'eri'))
Metaluokka
AfterInitWrapper
perii kaikki luokan type
ominaisuudet.Kun luokalle on määritetty metodi
__call__
, voi luokan ilmentymiä kutsua kuin funktioita. Tässä cls
on luokka, jonka
ilmentymää olemme luomassa. Esimerkiksi luokan Circle
yläluokan Shape
metaluokka on AfterInitWrapper
, joten kutsu
Circle((10.0, 20.0), 2.5, crimson) ohjautuu metodiin AfterInitWrapper.__call__
.Kutsumme metaluokan
type
alkuperäistä metodia __call__
, joka huolehtii olion luomisesta ja luokan Circle
luontimetodin __init__
kutsumisesta.Kutsumme luodulle oliolle
obj
metodia __after_init__
.Metodi
__after_init__
kutsuu metodia compute_area
ja asettaa tuloksen ominaisuusmuuttujaan self.area
.
Circle((10.0, 20.0), 2.5, crimson)
. Luokka, kutenCircle
on myös olio ja täten jonkin luokan instanssi. Kyseistä luokkaa kutsutaan metaluokaksi. Normaalisti luokan metaluokka ontype
, mutta metaluokan voi myös vaihtaa määrittämällä sen luokan otsikossa. Tässä määritämme, että luokanShape
metaluokka onAfterInitWrapper
.