Hallo, wir möchten in einer Klasse eine Reihe von Attributen konsistent speichern; d.h. es gibt Beschränkungen für die verschiedenen Attribute. Die Idee ist, das durch Properties zu machen: Class Cfg: def __init__(self); self._myprop = 1.23 [...] @property def myprop(self) return self._myprop @myprop.setter def myprop(self, val): if val > self._maxprop: # als Beispiel raise ValueError() self._myprop = val Das Unschöne daran ist, dass die Initialisierung der Property im __init__ und damit physisch weit entfernt vom Rest erfolgt. Das ist, wenn man viele (>>20) Attribute hat, nicht mehr übersichtlich. 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? Schöne Grüße Ole
Hallo Ole, leider habe ich selbst auch noch keine Erfahrung mit der Bibliothek gemacht und ich bin mir auch nicht sicher, ob sie deinen Fall abdeckt, aber kennst du pydantic? https://pydantic-docs.helpmanual.io/usage/validators/ Falls deine Klasse nur ein Datenkontainer ist, könnte ich mir gut vorstellen, dass die Bibliothek das macht, was du willst, falls der Aspekt nur ein kleiner Aspekt ist, ist pydantic vllt. doch nichts für dich. Viele Grüße, Julian On 26/05/2020 17:44, Оlе Ѕtrеісhеr wrote:
Hallo,
wir möchten in einer Klasse eine Reihe von Attributen konsistent speichern; d.h. es gibt Beschränkungen für die verschiedenen Attribute. Die Idee ist, das durch Properties zu machen:
Class Cfg: def __init__(self); self._myprop = 1.23
[...]
@property def myprop(self) return self._myprop
@myprop.setter def myprop(self, val): if val > self._maxprop: # als Beispiel raise ValueError() self._myprop = val
Das Unschöne daran ist, dass die Initialisierung der Property im __init__ und damit physisch weit entfernt vom Rest erfolgt. Das ist, wenn man viele (>>20) Attribute hat, nicht mehr übersichtlich.
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?
Schöne Grüße
Ole _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de
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. 2. Wie ruft man die iniatlizer überhaupt auf? -- Schönen Gruß Hartmut Goebel Dipl.-Informatiker (univ), CISSP, CSSLP, ISO 27001 Lead Implementer Information Security Management, Security Governance, Secure Software Development Goebel Consult, Landshut http://www.goebel-consult.de Blog: https://www.goe-con.de/blog/dsl-fernkonfiguration-ist-kritisch-fur-den-daten... Kolumne: https://www.goe-con.de/hartmut-goebel/cissp-gefluester/2011-02-fleisige-date...
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...
Am 27.05.20 um 17:13 schrieb Peter Otten:
Die Reihenfolge kann durch die Positionierung in der Klasse bestimmt werden:
Könnte man machen, allerdings würde ich mich nicht darauf verlassen, dass die Reihenfolge in `vars(A).keys()` immer stimmt. Kannst Du mit eine Doku dazu zeigen, ich habe nichts gefunden. Allerdings hat mich Deine Mail noch auf einen ganz anderen Ansatz gebracht: class A: _myprop = 1.23 @property def myprop(self): return self._myprop @myprop.setter def myprop(self, value): if value < 2.5: self._myprop = value a = A() print(a.myprop) a.myprop = 2.1 print(a.myprop) -- Schönen Gruß Hartmut Goebel Dipl.-Informatiker (univ), CISSP, CSSLP, ISO 27001 Lead Implementer Information Security Management, Security Governance, Secure Software Development Goebel Consult, Landshut http://www.goebel-consult.de Blog: https://www.goe-con.de/blog/35.000-gegen-vorratdatenspeicherung Kolumne: https://www.goe-con.de/hartmut-goebel/cissp-gefluester/2011-09-kommerz-uber-...
Am So Mai 31 2020, 10:13:43 schrieb Hartmut Goebel:
Am 27.05.20 um 17:13 schrieb Peter Otten:
Die Reihenfolge kann durch die Positionierung in der Klasse bestimmt werden: Könnte man machen, allerdings würde ich mich nicht darauf verlassen, dass die Reihenfolge in `vars(A).keys()` immer stimmt. Kannst Du mit eine Doku dazu zeigen, ich habe nichts gefunden.
Was Knackiges kann ich auch nicht vorweisen, nur https://docs.python.org/3/whatsnew/3.6.html#pep-520-preserving-class-attribu... mit dem Verweis auf die PEP 520 – Preserving Class Attribute Definition Order https://www.python.org/dev/peps/pep-0520 bei deren Enstehung wohl noch nicht bekannt war, dass `dict`s die insertion order erhalten.
Allerdings hat mich Deine Mail noch auf einen ganz anderen Ansatz gebracht:
class A: _myprop = 1.23
@property def myprop(self): return self._myprop
@myprop.setter def myprop(self, value): if value < 2.5: self._myprop = value
a = A() print(a.myprop) a.myprop = 2.1 print(a.myprop)
Das hat den Vorteil, dass man sich nur auf Standard-Konstrukte verlässt. Für mutable defaults muss man das allerdings modifizieren.
Am 26.05.2020 um 17:44 schrieb Оlе Ѕtrеісhеr:
Hallo,
wir möchten in einer Klasse eine Reihe von Attributen konsistent speichern; d.h. es gibt Beschränkungen für die verschiedenen Attribute. Die Idee ist, das durch Properties zu machen:
Also die Properties verwirren mich hier. Das Beispiel an einer Stelle auch... [siehe etwas weiter unten] Darum eine andere Frage: Gelten die Beschränkungen eines Attributs für alle Instanzen der Klasse? Falls Ja, die Beschränkungen als Klassenattribute einfach hinschreiben? class Cfg(object): def __init__(self): self._myprop = 1.23 # hier wirklich .myprop ? @property def myprop(self): return self._myprop _maxprop = 1.24 @myprop.setter def myprop(self, val): if val > self._maxprop: # als Beispiel raise ValueError() self._myprop = val if __name__ == '__main__': versuch = Cfg() print(versuch.myprop) # gibt 1.23 aus versuch.myprop = 1 print(versuch.myprop) # gibt 1 aus versuch.myprop = 1.25 # wirft ValueError print(versuch.myprop) Grüße, Andreas
Class Cfg: def __init__(self); self._myprop = 1.23
[...]
@property def myprop(self) return self._myprop
@myprop.setter def myprop(self, val): if val > self._maxprop: # als Beispiel raise ValueError() self._myprop = val
Das Unschöne daran ist, dass die Initialisierung der Property im __init__ und damit physisch weit entfernt vom Rest erfolgt. Das ist, wenn man viele (>>20) Attribute hat, nicht mehr übersichtlich.
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?
Schöne Grüße
Ole
Hallo allerseits, hallo Ole, evtl. könnte man das auch mit dem descriptor-protokoll lösen, so ha ich das bei einem sehr aufwendigen Projekt gemacht. Sehr gutes Tutorial (englisch): https://www.youtube.com/watch?v=sPiWg5jSoZI update vom selben Autor: https://www.youtube.com/watch?v=5nXmq1PsoJ0 https://docs.python.org/3/howto/descriptor.html Dann kannst du sowas machen: class Foo: myprop = Cfg(init=1.23) Grüße Gregor Am 26.05.20 um 17:44 schrieb Оlе Ѕtrеісhеr:
Hallo,
wir möchten in einer Klasse eine Reihe von Attributen konsistent speichern; d.h. es gibt Beschränkungen für die verschiedenen Attribute. Die Idee ist, das durch Properties zu machen:
Class Cfg: def __init__(self); self._myprop = 1.23
[...]
@property def myprop(self) return self._myprop
@myprop.setter def myprop(self, val): if val > self._maxprop: # als Beispiel raise ValueError() self._myprop = val
Das Unschöne daran ist, dass die Initialisierung der Property im __init__ und damit physisch weit entfernt vom Rest erfolgt. Das ist, wenn man viele (>>20) Attribute hat, nicht mehr übersichtlich.
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?
Schöne Grüße
Ole _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de
participants (6)
-
Andreas B. -
Gregor Engberding -
Hartmut Goebel -
Julian Gethmann -
ole-usenet-spam@gmx.net -
Peter Otten