Rezepte

Wechselseitiger Dispatch

Inhaltsverzeichnis

  1. Problemstellung
  2. Parametrisierte Module
  3. Globale Variablen
  4. Vererbung

Problemstellung

Wir betrachten Eierzahlen, die sich verhalten wie gewöhnliche ganze Zahlen, aber von diesen zu unterscheiden sind. Es ist eine allgemeine Nachfolgerabbildung zu formulieren, die sowohl für gewöhnliche als auch für Eierzahlen definiert ist. Weiterhin soll diese Abbildung polymorph formuliert sein, dergestalt dass die passende Abbildung über einen Dispatch ermittelt wird. Innerhalb der Abbildung soll sie nochmals aufgerufen werden, so dass ein wechselseitiger Dispatch stattfinden kann. Ausgangspunkt ist so gesagt das folgende Programm:

# Datei egg.py
class Egg:
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return "Egg({})".format(repr(self.value))
    def __succ__(self):
        return Egg(succ(self.value))
        
# Datei succ.py
def succ(x):
    return x + 1 if isinstance(x, int) else x.__succ__()

# Datei app.py
from egg import Egg
from succ import succ

print(succ(Egg(0)))

Hier stellt sich das Problem, dass succ in Egg unbekannt ist. Man könnte also succ einfach importieren. Wir würden nun aber gerne haben, dass egg nichts über succ wissen braucht und umgekehrt succ nichts über egg wissen braucht.

Parametrisierte Module

Ein Lösungsansatz besteht in der Formulierung eines parametrisierten Moduls. Hier dürfen dem Modul beim Laden zusätzliche Parameter gegeben werden.

# Datei egg.py
def load(succ = None):
    class Egg:
        def __init__(self, value):
            self.value = value
        def __repr__(self):
            return "Egg({})".format(repr(self.value))
    if succ != None:
        def __succ__(self):
            return Egg(succ(self.value))
        Egg.__succ__ = __succ__
    return Egg

# Datei succ.py
def succ(x):
    return x + 1 if isinstance(x, int) else x.__succ__()

# Datei app.py
import egg
from succ import succ
Egg = egg.load(succ = succ)

print(succ(Egg(0)))

Globale Variablen

Parametrisierung kann alternativ über globale Variablen stattfinden. Im der nächsten Formulierung ist module_table eine von jedem Modul aus zugängliche globale Variable. Es tut sich hier leider das kleine Defizit auf, dass die Reihenfolge, in der die Module importiert werden, nun eine Rolle spielt, was dem Nutzer der Programmierschnittstelle nicht unbedingt klar sein muss.

# Datei config.py
module_table = {}

# Datei egg.py
import config

class Egg:
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return "Egg({})".format(repr(self.value))

if "succ" in config.module_table:
    succ = config.module_table["succ"].succ
    def __succ__(self):
        return Egg(succ(self.value))
    Egg.__succ__ = __succ__

# Datei succ.py
import config
config.module_table[__name__] = __import__(__name__)

def succ(x):
    if isinstance(x, int):
        return x + 1
    else:
        return x.__succ__()

# Datei app.py
from succ import succ
from egg import Egg

print(succ(Egg(0)))

Vererbung

Eine recht ordentliche Konstruktion findet sich, wenn man für die zusätzliche Funktionalität eine extra Klasse definiert, die die gewöhnliche Eierklasse als Basis hat. Es verbleibt das kleine Defizit, dass bei der Nutzung der Programmierschnittstelle die passende Konfiguration gewählt werden muss.

# Datei egg.py
class Egg:
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return "Egg({})".format(repr(self.value))

# Datei eggsucc.py
from egg import Egg as PlainEgg
from succ import succ

class Egg(PlainEgg):
    def __succ__(self):
        return Egg(succ(self.value))

# Datei succ.py
def succ(x):
    return x + 1 if isinstance(x, int) else x.__succ__()

# Datei app.py
from eggsucc import Egg
from succ import succ

print(succ(Egg(0)))