Metaprogramowanie to najprościej rzecz ujmując pisanie programów, które piszą programy. Lekki wstęp można znaleźć tutaj wikipedia:Metaprogramming, a całą koncepcję najłatwiej wyjaśnić na przykładzie LISPA, w którym program piszę się bezpośrednio w drzewie składniowym a kod programu jest listą. Zatem przy niewielkiej zmianie w syntaktyce można stworzyć konstrukcje (tutaj: makro), które w momencie uruchomienia generuje inną listę i ją wywołuję jako program.
No dobra, ale po co to wszystko? Okazuje się, że to bardzo potężny mechanizm, a Paul Graham twierdzi, że jego programy w LISP były złożone w około 40% z makr, co pozwalało mu tworzyć web-dwa-zero projekt (który został ostatecznie wykupiony przez Yahoo) znacznie szybciej niż jego ówczesna konkurencja.
No wielkim zaskoczeniem nie będzie, że metaprogramowanie zostało dorzucone również do Pythona, a konstrukcja, która na to pozwala to Metaclasses. Na sam początek:
Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). -- Tim Peters
A więc motywacje już mamy (!)
Na początek problem: Stworzyć interpreter zapytań podobnych do tych tutaj sbql:examples. Dodatkowo niech program nie będzie dłuższy niż cztery ekrany. Oczywiście podobieństwo naszych zapytań do zapytań języka SBQL jest mocno naciągane, niemniej jednak przyjrzyjmy się:
class Osoba():
__metaclass__ = pql_memorystore
imie = Attribute(str, "[*..2]")
nazwisko = Attribute(type = str, card = "[1..1]")
wiek = Attribute(int)
def __init__(self, imie, nazwisko):
self.imie = imie
self.nazwisko = nazwisko
w drugiej linijce określamy tylko metaklasę dla naszej, klasy co sprawia, że nasza żmudnie tworzona klasa Osoba po zakończeniu jej definicji staje sie już zupełnie czymś innym. A w naszym przypadku będzie listą. Czyli mamy klasę, która jest jednocześnie listą i dodatkowo każda nowa instancja tej klasy odkłada się do siebie... weird kind of magic
hmmm sprawdźmy:
Możemy zatem tworzyć obiekty:
o1 = Osoba(imie = "Adam", nazwisko = "Janskowski")
o2 = Osoba(imie = "Janek", nazwisko = "Wlodarczyk")
Osoba("Sigmun", "Freud")
Osoba(imie = "Adam", nazwisko = "Malysz")
Osoba("Tolek", "Skoczylas")
i wykonywać na nich coś w rodzaju zapytań, które operują na instancjach własnych klas:
Osoba
Osoba.imie
[o for o in Osoba if o.imie == ['Adam']]
pql_list([o for o in Osoba if o.imie == ['Adam']]).Nazwisko
Pewnie podobieństwo do SBQL można by jeszcze trochę podciągnąć w ramach samego Pythona przeciążając operatory; znacznie więcej dałoby rade pewnie podciągnąć pewnie evalem, ale przecież to nie o to chodziło.
Program jest tutaj. Napisałem go dawno temu (w czasach gdy jeszcze pterodaktyle latały po niebie) i nigdy nie skończyłem ale w sumie mniej więcej działa, a w pliku jest więcej przykładów. Jest tam nawet wstęp do stworzenia kontroli typów i kardynalności atrybutów.
Brak komentarzy:
Prześlij komentarz