Dwemthy's Array or Eat this, Ruby!

Code-Stücke können hier veröffentlicht werden.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Durch die Blogsphere geistert gerade Why the Lucky Stiffs Artikel über Ruby-Metaprogrammierung, wo er in unnachahmlicher - aber recht konfuser - Weise Ruby Metaprogrammierung preist.

Ich finde, das ganze geht in Python viel einfacher :) Oder ich habe das Problem nicht verstanden. Zwei Punkte sind wichtig:

Man möchte gerne Klassen wie Prototypen definieren, sodass alle Exemplare einer Klasse automatisch gewisse Initialwerte haben:

Code: Alles auswählen

class Dragon < Creature
  life 1340     # tough scales
  strength 451  # bristling veins
  charisma 1020 # toothy smile
  weapon 939    # fire breath
end
Man möchte ein Array (das von Dwemthy) definieren, welches sich wie eine Creature verhält und alle Nachrichten, die man dem Array schickt (das ist Smalltalk- und Ruby-Sprech für Methodenaufrufe) an das erste Array-Element weiterreichen. Dazu überschreibt _why Rubys `method_missing` Methode einer Unterklasse von Array.

In Python sieht der Drache so aus:

Code: Alles auswählen

class Dragon(Creature):
    life = 1340     # tough scales
    strength = 451  # bristling veins
    charisma = 1020 # toothy smile
    weapon = 939    # fire breath
Damit die automatische Vorbelegung von Exemplaren mit den Standardwerten möglichst einfach funktioniert und damit ich's genauso mache wie Ruby, benutze ich eine Metaklasse:

Code: Alles auswählen

class MetaCreature(type):
    def __init__(cls, name, bases, dct):
        cls.__traits__ = getattr(bases[0], '__traits__', ()) + tuple(dct.pop('traits', ()))

class Creature(object):
    __metaclass__ = MetaCreature
    
    traits = ('life', 'strength', 'charisma', 'weapon')
    
    def __init__(self):
        for trait in self.__traits__: setattr(self, trait, getattr(self, trait))
Auch das Array, das in Python natürlich eine Dwemthy's List ist, ist keine große Magie. Ich überschreibe __getattr__ um mitzubekommen, wenn nach etwas gesucht wird, das es nicht in `list` gibt. Ich muss ein bisschen aufpassen, weil es in Python nicht nur Methoden gibt, sondern Funktionen und Properties.

Code: Alles auswählen

class DwemthysList(list):
    def __getattr__(self, name):
        if len(self):
            value = getattr(self[0], name)
            if callable(value):
                def cont(*args, **kwargs):
                    answer = value(*args, **kwargs)
                    ...
                    return answer
                cont.__name__ = name
                return cont
            return value
Der vollständige Code ist hier...

Geht es noch kürzer? Ich hätte ja gerne eine traits()-Methode innerhalb der Klasse benutzt, aber wie manipuliere ich in diese Funktion dann die Klasse? Sie existiert ja noch gar nicht, sondern es gibt nur ein namenloses Modul, an das ich AFAIK auch nicht herankomme. Evtl. könnte ich mir den Parent-Frame des aktuellen Frames holen und dort das locals-Dict, doch das ist auch häßlich. Meine erste Version hat auf die traits verzichtet, dann kann ich jedoch nicht mehr sauber Standardwerte und Methoden der Klasse unterscheiden. Ein `callable`-Test erschien mir unsauber.

Stefan
Antworten