Am Mi Mai 27 2020, 10:27:31 schrieb Hartmut Goebel:
Am 26.05.20 um 17:44 schrieb Оlе Ѕtrеісhеr:
Wie bringt man die enger zusammen? Ein @myprop.init gibt es ja offensichtlich nicht und kann man auch nicht so ohne weiteres anlegen, oder? Oder was wäre ein sinnvolles Pattern hier?
Du könntest eine eigene "Property" class implementieren, die einen weiteren Docorator "initialisiere" hat. Allerdings bleibt immer noch das Problem, dass Du diese Funktion in __init__ irgendwie aufrufen musst. Du müsstest in __init__ hergehen und alle properies der Klasse (self.__class__) finden und deren initalizer aufrufen.
Ich habe die genau Funktionsweise von Properties nicht im Kopf, aber irgendwie sollte das gehen. Vielleicht schaust Du man in den PEP zu properties, dort gibt es sicher Details.
Allerdings gibt es mindestens zwei Haken:
1. Wenn die initializer in eine bestimmten Reihenfolge aufgerufen werden müssen, dann musst Du sie doch wieder benennen. Denn sonst bekommstt Du keine Reihenfolge.
Die Reihenfolge kann durch die Positionierung in der Klasse bestimmt werden:
class A: ... foo = 1 ... bar = 2 ... class B: ... bar = 2 ... foo = 1 ... vars(A).keys() dict_keys(['__module__', 'foo', 'bar', '__dict__', '__weakref__', '__doc__']) vars(B).keys() dict_keys(['__module__', 'bar', 'foo', '__dict__', '__weakref__', '__doc__'])
2. Wie ruft man die iniatlizer überhaupt auf?
Da benötigt man die Mithilfe der Eigentümer(meta)klasse. Nachfolgend eine einfache Implementierung, die die Properties anhand ihrer Klasse identifiziert: $ cat init_property.py missing = object() class myproperty(property): finit = None default = missing def initializer(self, f): self.finit = f return self def init(self, owner): if self.finit is not None: self.finit(owner) elif self.default is not missing: # eventuell "_" + self.name, um den Aufruf des Setters # zu vermeiden setattr(owner, self.name, self.default) def __set_name__(self, owner, name): self.name = name class Cfg: @classmethod def myproperties(cls): return [p for p in vars(cls).values() if isinstance(p, myproperty)] def __init__(self): for p in self.myproperties(): p.init(self) @myproperty def foo(self): return self._foo @foo.setter def foo(self, value): assert 0 <= value < 100 self._foo = value @foo.initializer def foo(self): self._foo = 42 @myproperty def bar(self): return self._bar @bar.setter def bar(self, value): assert isinstance(value, int) self._bar = value bar.default = 123 c = Cfg() print(c.foo) # 42, im Initializer gesetzt print(c.bar) # 123, default-Attribut c.foo = 24 # OK c.bar = "spam" # Exception $ python3.8 init_property.py 42 123 Traceback (most recent call last): File "init_property.py", line 59, in <module> c.bar = "spam" File "init_property.py", line 50, in bar assert isinstance(value, int) AssertionError $ Wer's gerne eleganter hätte, könnte sich ja mal den Quellcode der dataclasses zu Gemüte führen...