Neulich bin ich über die Tatsache gestolpert, dass in Python callable nicht gleich callable ist. In Klassendefinitionen werden nämlich nur „echte“ Funktionen zu Methoden. Gegeben seien folgende unschuldige Klassen:
Code: Alles auswählen
class Value(object):
from_str = None
to_str = str
def __init__(self, value):
self.value = self.from_str(value)
def __str__(self):
return self.to_str(self.value)
class Integer(Value):
from_str = int
Funktioniert super:
Dann bin ich aber auf die doofe Idee gekommen für meine beiden „Factory-Funktionen“ wirklich Funktionen zu verwenden:
Code: Alles auswählen
class Boolean(Value):
from_str = lambda value: bool(int(value))
to_str = lambda value: str(int(value))
Code: Alles auswählen
>>> assert str(Boolean("1")) == "1"
TypeError: <lambda>() takes exactly 1 argument (2 given)
Upps, da hab ich wohl ausversehen eine Methode definiert(hätte ich mir denken können). Aber dann frage ich mich, warum das vorher geklappt hat. Wäre es nicht wesentlich konsistenter wenn Python nicht zwischen zwei callables unterscheiden würde? Vor allem da das nicht nur mit Typen funktioniert, sondern auch mit allen anderen Objekten die „callable“ aber keine echte Funktion sind.
Code: Alles auswählen
class Integer(Value):
from_str = functools.partial(base=8)
Funktioniert wider erwarten, aber auch nur, weil partial als Instanz einer Klasse und nicht als closure implementiert wurde(edit: war das ein Trugschluss, ich stelle gerade fest, dass das Ergebnis von partial wirklich den selben Typ wie eine Funktion hat). Ich finde das sehr unintuitiv und für eine dynamisch typisierte Sprache im allgemeinen auch tödlich, wenn man sich so an Typen klammert. An der Stelle bin ich übrigens gleich in die 2. Falle getappt. Ich hielt mich für besonders schlau das Problem einfach wie folgt zu umgehen:
Code: Alles auswählen
class Value(object):
from_str = None
to_str = str
def __init__(self, value):
self.value = self.__class__.from_str(value)
def __str__(self):
return self.__class__.to_str(self.value)
Denkste! In Python 2.x gibt es ja noch die „unbound“-Method die einen völlig unnötigen Typcheck auf das erste übergebene Argument macht. Mich würde mal interessieren wozu man überhaupt „unbound methods“ und erst recht diesen Typcheck macht?
Das 2. Problem wurde ja mit Python 3 abgehandelt(auch wenn ich stark dafür wäre das auch in 2.x zu beseitigen). Wie denkt ihr über die 1. Sache, sollte man Python derart abändern, dass alle callables die Attribute von Klassen sind in der Instanz zu Methoden werden? Vorteil: Wäre nur konsistent und logisch(duck typing). Nachteil: Wird sicherlich existierenden Code geben der sich darauf verlässt, dass dies nicht so ist.