properties aus klassen "lösen"

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Mich interessiert, ob man ein Property eines Objekts an eine Variable binden kann.
Also was ich meine ist folgendes:

Code: Alles auswählen

class C(object):
    def __init__(self):
        self._x = -1
        self._y = -1
    
    def y(self):
        self._y += 1
        return self._y
    
    @property
    def x(self):
        self._x += 1
        return self._x
        
c = C()

x = c.x #was kann man hier machen?
y = c.y

print x, x, x
print y(), y(), y()

Nun soll ``x,x,x`` das gleiche ergeben wie ``y(), y(), y()``. Ist das möglich?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Ich glaube nicht, dass das geht (und ich sehe auch nicht wozu es gut sein soll).
Sowas könnte gehen:

Code: Alles auswählen

class Foo(object): #ich hänge noch an 2.6 ;)
  def foo(self):
    return 42
  x = property(foo)

f = Foo()
y = f.foo
print f.x, y()
property() kann ja noch mehr, als bloß dekorieren.
hth, Jörg

ps.: Beschreibe mal was du vorhast...
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Vor habe ich nichts. Nur habe ich mich gefragt, ob das möglich ist.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Du kannst über die Klasse an das property kommen und darauf fget aufrufen.

Code: Alles auswählen

>> class Foo(object):
..     x = property(lambda self: 1)
>> Foo.x.fget(Foo())
 > 1
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Ob es möglich ist, könntest du - für deine Implementierung des Python-Interpreters - herausfinden, in dem du diesen einfach startest und es ausprobierst. Ob das jetzt ein vorgesehenes Verhalten ist, sprich ob es auch garantiert ist, daß es mit einer neuen Version nicht einfach so verschwindet, steht auf einem anderen Blatt. Die Sprachspezifikation müßte hierüber Auskunft geben können.

Bleibt natürlich noch die Frage, ob es überhaupt Sinn macht, so etwas zu tun. Und ob es da nicht andere, weniger verdeckte Wege gibt. Meiner Meinung nach sollte die Eigenschaft eines Objektes auch über objekt.eigenschaft angesprochen werden, damit ganz klar ist, was da eigentlich passiert. Ansonsten wird eine Variable manipuliert und du hast plötzlich ein verändertes Objekt an einer Stelle, an der du es nicht erwartest. Sowas kann die Fehlersuche zur Hölle machen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Pekh hat geschrieben:Ob es möglich ist, könntest du - für deine Implementierung des Python-Interpreters - herausfinden, in dem du diesen einfach startest und es ausprobierst.
Besser ist aber, es anhand der Sprachspezifikation abzuleiten bzw. zu wissen. Nix gegen probieren, aber die Grenzen einer Programmiersprache durch trial und error zu lernen erscheint mir, sagen wir mal, suboptimal.

Und bei Python (im Gegensatz zu z.B. Ruby) ist es so, dass Funktionsaufrufe immer explizit sind. Daher kann "x" niemals ein in "x" enthaltenes Funktionsobjekt aufrufen. Descriptoren, die einen impliziten Methodenaufruf implizieren, funktionieren nur zusammen mit einem Attributzugriff.

Stefan
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

@DasIch:
Das ist mal gut zu wissen, falls man ein property 'reparieren' muss (fällt doch wohl auch unter monkey-patching, oder?)

@Pekh.
Ein Property (in python) ist nicht bloß eine normale Eigenschaft, sondern eine berechnete (Klassiker: das Alter von einem Objekt, wenn das Objekt einen Erzeugungs-Timestamp als Eigenschaft hat).

hth, Jörg
ps.: Ein Grund ist mir eingefallen: wenn man den 'look-up' (den Punkt) aus einer seeeeeeehr oft zu durchlaufenden Schleife rausnehmen will - aber da ist evtl. eine Lösung gleich ohne property besser.
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

@b.esser-wisser:
Mir ist durchaus bekannt, daß eine property eine "berechnete Eigenschaft" ist. Aber letztlich wird die Berechnung ja versteckt und die eigentlich dahinterstehenden Funktionen treten als gewöhnliche Eigenschaft auf. Und das, was ich bezüglich der Nachvollziehbarkeit geschrieben habe gilt natürlich auch für die Methoden des Objekts.

Das mit dem Monkey-Patching ist so ne Sache. Wenn man sich nicht alle möglichen Seiteneffekte einhandeln will, braucht man einiges Wissen um Details der Implementierung. Wenn man aber weiß, daß es sich bei der Eigenschaft nicht um ein gewöhnliches Attribut sondern um eine Property handelt, kann man eigentlich auch direkt die zugrundeliegende Methode manipulieren. Generell würde ich aber, insbesondere auch im Zusammenhang mit Unit-Tests, davon abraten, Monkey-Patching zu verwenden. Es zerstört die Unabhängigkeit der Tests voneinander (z.B. weil man vergisst, eine Manipulation rückgängig zu machen). Bei den Google Tech Talks gibt es eine Reihe von Vorträgen ("Clean Code Talks"), die die Problematik sehr schön verdeutlichen.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Pekh hat geschrieben:Wenn man aber weiß, daß es sich bei der Eigenschaft nicht um ein gewöhnliches Attribut sondern um eine Property handelt, kann man eigentlich auch direkt die zugrundeliegende Methode manipulieren.
Das geht nur wenn dass property den Wert tatsächlich von einer Methode bekommt und der Name dieser Methode aus dem Namesraum der Klasse nicht gelöscht wurde.
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

DasIch hat geschrieben:
Pekh hat geschrieben:Wenn man aber weiß, daß es sich bei der Eigenschaft nicht um ein gewöhnliches Attribut sondern um eine Property handelt, kann man eigentlich auch direkt die zugrundeliegende Methode manipulieren.
Das geht nur wenn dass property den Wert tatsächlich von einer Methode bekommt und der Name dieser Methode aus dem Namesraum der Klasse nicht gelöscht wurde.
So langsam komme ich nicht mehr mit ... Mir will gerade keine Möglichkeit einfallen, warum ein Property seinen Wert *nicht* von einer Methode / Funktion bekommen sollte. Schließlich sind die Teile doch genau dafür gedacht, einen faktischen Methodenzugriff wie einen direkten Attributzugriff darzustellen?

Und ganz ehrlich: Wenn ich anfange, Methoden aus dem Namenraum einer Klasse zu entfernen und diese dann später wieder reinzufrickeln mache ich irgendwas falsch. Was auch immer einen zu sowas bewegen sollte - es geht bestimmt auch sauberer.
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Hier greift dann nur noch das "We are all consenting adults: Man nimmt nicht automatisch properties, nur weil's geht (das gilt ja praktisch für alles in Python: (Meta-)Klassen, Mehrfachvererbung, Zugriff auf "_attribute", etc.).
Und 'monkey-patching' benutzt man auch nicht jeden Tag (eher nie) - kann aber extrem hilfreich gegen akute Bugs sein.
Pekh hat geschrieben: Schließlich sind die Teile doch genau dafür gedacht, einen faktischen Methodenzugriff wie einen direkten Attributzugriff darzustellen?
Ich sehe es eher umgekehrt: Die properties (in python) sind hauptsächlich 'syntactic sugar', um nachträglich z.B. Tests/Validierungen einzubringen, anstatt immer[ (get-/set-)Methoden zu nehmen.
Die Methodenaufrufe (und eben auch Propertys)sind in Python eben viel teuerer als 'bloße' Attributzugriffe, also wird versucht, sie möglichst zu vermeiden - durch 'public' Attribute eben.
Pekh hat geschrieben:Und ganz ehrlich: Wenn ich anfange, Methoden aus dem Namenraum einer Klasse zu entfernen und diese dann später wieder reinzufrickeln mache ich irgendwas falsch.
Ist natürlich grundsätzlich richtig, aber der "."-operator in Python holt ja (meistens) den Namen zur Laufzeit aus einem dict, das kostet (messbar) Zeit - die man bei vielen Wiederholungen auch spürt.
hth, Jörg
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

b.esser-wisser hat geschrieben:Hier greift dann nur noch das "We are all consenting adults: Man nimmt nicht automatisch properties, nur weil's geht
Ja, das sehe ich auch so. Ich verstehe irgendwo auch den Wunsch, die Grenzen auszuloten, aber es ist halt eine sehr theoretische Problemstellung, die in der Praxis keine Bedeutung haben sollte. Wenn man es doch brauchen sollte, ist es meiner Meinung nach ein Indiz dafür, daß im Design etwas nicht stimmt. Entweder im eigentlichen Code oder im Aufbau des Tests. Oder und. :)
b.esser-wisser hat geschrieben: Ist natürlich grundsätzlich richtig, aber der "."-operator in Python holt ja (meistens) den Namen zur Laufzeit aus einem dict, das kostet (messbar) Zeit - die man bei vielen Wiederholungen auch spürt.
hth, Jörg
Und wenn ich das selbe Objekt (Methode oder Attribut, egal) an eine lokale Variable binde, wird es nicht aus einem Dict geholt? Wo werden denn die lokalen Variablen verwaltet?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@Pekh

Code: Alles auswählen

class Foo(object):
    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, new_foo):
        self._foo = new_foo

    @apply
    def bar():
        def get(self):
            return self._bar
        def set(self, new_bar):
            self._bar = new_bar
        return property(get, set)

    def _get_baz(self):
        return self._baz

    def _set_baz(self, new_baz):
        self._bar = new_baz
    baz = property(_get_baz, _set_baz)
    del _get_baz, _set_baz
Damit hätten wir schon drei Beispiele bei denen man keinen direkten Zugriff auf getter/setter Methoden hat und keine von denen ist sonderlich ungewöhnlich.
pekh hat geschrieben:Und wenn ich das selbe Objekt (Methode oder Attribut, egal) an eine lokale Variable binde, wird es nicht aus einem Dict geholt? Wo werden denn die lokalen Variablen verwaltet?
Der unterschied bei einem Attribut ist dass erst das Objekt aus dem lokalen namespace geholt wird und dann dass Attribut aus dem namespace des Objektes geholt werden muss. Das kostet durchaus viel Zeit.
Zuletzt geändert von DasIch am Donnerstag 24. September 2009, 22:58, insgesamt 1-mal geändert.
BlackJack

@Pekh: Eine Variante ohne Methoden aus der Klasse zu löschen *und* ohne dass man so einfach auf die ursprünglichen Funktionen/Methoden für das Property zugreifen kann:

Code: Alles auswählen

class X(object):
    def __init__(self, spam):
        self._spam = spam
    
    @apply
    def spam():
        def fget(self):
            print 'get'
            return self._spam
        def fset(self, value):
            print 'set'
            self._spam = value
        doc = 'some docs'
        return property(**locals())
Lokale Namen werden, zumindest bei CPython, nicht in einem Dictionary gespeichert, sondern in einer Liste und jeder Name wird vom Compiler in einen Index in diese Liste übersetzt. Der Zugriff ist also mit deutlich weniger Aufwand verbunden. Sieht man im folgenden an den ``STORE_FAST`` und ``LOAD_FAST`` Bytecodes im Gegensatz zum ``LOAD_GLOBAL`` bei `c`.

Code: Alles auswählen

In [50]: def f():
   ....:     a = 42
   ....:     b = 23
   ....:     print a, b, c
   ....:

In [51]: import dis

In [52]: dis.dis(f)
  2           0 LOAD_CONST               1 (42)
              3 STORE_FAST               0 (a)

  3           6 LOAD_CONST               2 (23)
              9 STORE_FAST               1 (b)

  4          12 LOAD_FAST                0 (a)
             15 PRINT_ITEM
             16 LOAD_FAST                1 (b)
             19 PRINT_ITEM
             20 LOAD_GLOBAL              0 (c)
             23 PRINT_ITEM
             24 PRINT_NEWLINE
             25 LOAD_CONST               0 (None)
             28 RETURN_VALUE
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Pekh hat geschrieben: Und wenn ich das selbe Objekt (Methode oder Attribut, egal) an eine lokale Variable binde, wird es nicht aus einem Dict geholt? Wo werden denn die lokalen Variablen verwaltet?
Da begeben wir uns in die 'internals' der (CPython-)VM - und damit kenne ich mich nicht wirklich aus, aber ich denke es gibt zwei Möglichkeiten:
a) Die lokalen Variablen liegen direkt auf dem Stack (~ der VM - nicht mit dem Stack der realen CPU verwechseln), dafür spricht, dass sich "locals()" anders verhält als "globals()"
b) die lokalen Variablen liegen immer noch in einem dict() - dann würde man nur einen 'look-Up' sparen.

Gute Nacht, Jörg
edit: da war ich mal echt langsam - und korrekt ;)
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Ah, ok. Wieder was gelernt. Aber dazu bin ich ja hier und lasse mich auf Diskussionen ein. ;) Vielen Dank auch.

Das die Zugriffszeiten doch so unterschiedlich sein können, hätte ich nicht gedacht. In diesem Fall mag das natürlich irgendwo alles Sinn machen. Trotzdem bleibt da irgendwo ein schaler Beigeschmack. Wäre es, wenn ich so viele gleichartige Manipulationen an ein und demselben Objekt vornehmen muß, nicht "hübscher", diese Manipulationen zu sammeln und dann nur noch einmal gesammelt durchzuführen? Oder die entsprechende Schleife o.ä. ins Objekt hineinzuverlegen?

Was die Properties betrifft: Ich verwende die so selten, daß ich mir noch nie Gedanken darum gemacht habe, daß man die auch in eine Meta-Funktion packen könnte. Wenn ich den Ansatz richtig verstanden habe, gibt es eine solche Funktion, die auf diese Weise alle properties defniert und die damit verbundenen setter und getter aus der Schnittstelle heraushält? In meinem Ringen um saubere Programmier-Muster ist das natürlich wieder ein schöner neuer Baustein. :) Und für das damit verbundene "Problem" gibt es bestimmt auch noch einen schönen Ansatz. Aber das muß wohl von Fall zu Fall entschieden werden.
BlackJack

@Pekh: Stimmt, hübsch ist das nicht gerade. Und ich würde so eine "Microoptimierung" auch nur machen, wenn ich dadurch nachweislich viel Zeit gewinne. Das ist haupächlich etwas für Wettbewerbe, wie SPOJ. :-)
Antworten