expertenaustausch > comp.lang.* > comp.lang.python

Оlе Ѕtrеісhеr (26.05.2020, 17:44)
Hallo,

wir mchten in einer Klasse eine Reihe von Attributen konsistent
speichern; d.h. es gibt Beschrnkungen fr 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 Unschne 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 wre ein sinnvolles Pattern hier?

Schne Gre

Ole
Julian Gethmann (26.05.2020, 18:07)
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?


Falls deine Klasse nur ein Datenkontainer ist, knnte 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 fr dich.

Viele Gre,
Julian

On 26/05/2020 17:44, ?l? ?tr???h?r wrote:
[..]
Hartmut Goebel (27.05.2020, 10:27)
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 wre ein sinnvolles Pattern hier?


Du knntest 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 msstest 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
mssen, dann musst Du sie doch wieder benennen. Denn sonst bekommstt Du
keine Reihenfolge.

2. Wie ruft man die iniatlizer berhaupt auf?
Andreas B. (27.05.2020, 13:18)
Am 26.05.2020 um 17:44 schrieb ?l? ?tr???h?r:
> Hallo,
> wir mchten in einer Klasse eine Reihe von Attributen konsistent
> speichern; d.h. es gibt Beschrnkungen fr 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 Beschrnkungen eines Attributs fr
alle Instanzen der Klasse?

Falls Ja, die Beschrnkungen 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)

Gre,
Andreas
[..]
Gregor Engberding (27.05.2020, 17:04)
Hallo allerseits, hallo Ole,

evtl. knnte man das auch mit dem descriptor-protokoll lsen, so ha ich
das bei einem sehr aufwendigen Projekt gemacht.
Sehr gutes Tutorial (englisch):

update vom selben Autor:




Dann kannst du sowas machen:
class Foo:
myprop = Cfg(init=1.23)

Gre
Gregor

Am 26.05.20 um 17:44 schrieb ?l? ?tr???h?r:
[..]
Peter Otten (27.05.2020, 17:13)
Am Mi Mai 27 2020, 10:27:31 schrieb Hartmut Goebel:
[..]
> 1. Wenn die initializer in eine bestimmten Reihenfolge aufgerufen werden
> mssen, 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 bentigt man die Mithilfe der Eigentmer(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 htte, knnte sich ja mal den Quellcodeder dataclasses
zu Gemte fhren...
Hartmut Goebel (31.05.2020, 10:13)
Am 27.05.20 um 17:13 schrieb Peter Otten:
> Die Reihenfolge kann durch die Positionierung in der Klasse bestimmt werden:


Knnte man machen, allerdings wrde 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)
Peter Otten (31.05.2020, 15:37)
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:

> Knnte man machen, allerdings wrde 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



mit dem Verweis auf die

PEP 520 ? Preserving Class Attribute Definition Order


bei deren Enstehung wohl noch nicht bekannt war, dass `dict`s die
insertion order erhalten.

[..]
> print(a.myprop)
> a.myprop = 2.1
> print(a.myprop)


Das hat den Vorteil, dass man sich nur auf Standard-Konstrukte verlsst.
Fr mutable defaults muss man das allerdings modifizieren.
hnliche Themen