Ohjelmoinnin peruskurssi Y2, kurssimateriaali

After init metaluokan avulla

Etusivulle

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'))
Olio luodaan funktiokutsulla, jossa kutsuttava funktio on luokka, esim. Circle((10.0, 20.0), 2.5, crimson). Luokka, kuten Circle on myös olio ja täten jonkin luokan instanssi. Kyseistä luokkaa kutsutaan metaluokaksi. Normaalisti luokan metaluokka on type, mutta metaluokan voi myös vaihtaa määrittämällä sen luokan otsikossa. Tässä määritämme, että luokan Shape metaluokka on AfterInitWrapper.
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.

Etusivulle