Seite 1 von 1

Properties gleichzeitig als (statische) Methoden verwenden

Verfasst: Donnerstag 18. September 2014, 09:29
von nezzcarth
Hallo,

ich bin in Objektorientierter Programmierung leider nicht besonders bewandert. Ich habe eine Klasse, die (vereinfacht) so aussieht:

(python3)

Code: Alles auswählen

class Test:
    def __init__(self, x, y, z):
        self.x=x
        self.y=y
        self.z=z

    @property
    def p1(self):
        return self.x*self.y*self.z
    
    @property
    def pn(self)
    ....
Ich habe mehrere solcher Properties, die teilweise voneinander abhängen und würde nun allerdings jedes @property auch gerne als Methode (bzw. Staticmethod?), verwenden können, wenn ich es mit Parametern aufrufe. Einerseits denke ich, dass das möglicherweise unsauber ist, andererseits würde ich so viel Code sparen. Gibt es da vielleicht eine Möglichkeit, das zu vereinfachen?

Vielen Dank :)

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 09:42
von BlackJack
@nezzcarth: Ich kann mir unter Beschreibung nichts vorstellen, beziehungsweise zu viel verschiedenes. Kannst Du mal ein bisschen konkreter werden?

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 09:50
von nezzcarth
@BlackJack:

Die Klasse sieht (leicht gekürzt) so aus; es handelt sich um die Berechnung von Spielwerten für ein Schiff, abhängig von den Maßen und der Anzahl der Masten.

Code: Alles auswählen

class Ship:
    def __init__(self, length, width, depth, masts, name="Nameless Ship"):
        self.length=length
        self.width=width
        self.depth=depth
        self.masts=masts
        self.name=name

    @property
    def tonnage(self):
        return round((self.length*self.width*self.depth)/3)

    @property
    def capacity(self):
        return round(self.tonnage*2/3)

    @property
    def crew(self):
        return divmod(self.tonnage, 10)[0] + self.masts*10
    
    def print_stats(self):
        print('Name: {0}'.format(self.name))
        print('Länge: {0}'.format(self.length))
        print('Breite: {0}'.format(self.width))
        print('Tiefgang: {0}'.format(self.depth))
        print('Masten: {0}'.format(self.masts))
        print('Schiffsraum: {0}'.format(self.tonnage))
        print('Frachtraum: {0}'.format(self.capacity))
        print('Mannschaft: {0}'.format(self.crew))
Ich würde jetzt gerne ein Property wie z.B. 'crew' zusätzlich optional auch wie eine 'normale' Funktion mit Parametern aufrufen können. Ich könnte natürlich auch eine eigene Funktion dafür schreiben, hätte aber eine Codeverdoppelung.

EDIT:

Code: Alles auswählen

#So verwende ich es:
>>> ship = Ship(20,6,2,3)
>>> ship.crew
38
>>> ship.tonnage
80
#Und so würde ich es gerne optinal auch verwenden können:
>>> ship.crew(80,2)
Ist das möglicherweise unsauber, weil die Instanz an sich ja auf ein konkretes Schiff referiert, der Aufruf mit Parametern aber ja generalisiert ist?

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 09:56
von Hyperion
nezzcarth hat geschrieben: Ich würde jetzt gerne ein Property wie z.B. 'crew' zusätzlich optional auch wie eine 'normale' Funktion mit Parametern aufrufen können. Ich könnte natürlich auch eine eigene Funktion dafür schreiben, hätte aber eine Codeverdoppelung.
Nö, Du kannst ja die Logik in eine *Funktion* schreiben, die Du in der Property lediglich aufrufst ;-)

Code: Alles auswählen

:class Ship:
:    def __init__(self, length, width, depth, masts, name="Nameless Ship"):
:        self.length=length
:        self.width=width
:        self.depth=depth
:        self.masts=masts
:        self.name=name
:
:    @property
:    def tonnage(self):
:        return tonnage(self.length, self.width, self.depth)
:
:def tonnage(length, width, depth):
:    return (length * width * depth) / 3
:
:--

In [21]: s = Ship(60, 20, 10, 3)

In [22]: s.tonnage
Out[22]: 4000.0

In [23]: tonnage(60, 20, 10)
Out[23]: 4000.0

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 09:57
von /me
nezzcarth hat geschrieben:Ich würde jetzt gerne ein Property wie z.B. 'crew' zusätzlich optional auch wie eine 'normale' Funktion mit Parametern aufrufen können.
Was sollen diese Parameter denn machen? Möchtest du unabhängig von einer konkreten Instanz Werte für tonnage und masts übergeben können?

Edit: Falls das gewünscht ist, dann ist BlackJacks Vorschlag die gewünschte Lösung.

Edit2: *gnarf* Hyperion war es natürlich ...

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 10:03
von Hyperion
/me hat geschrieben: Edit: Falls das gewünscht ist, dann ist BlackJacks Vorschlag die gewünschte Lösung.
Ui... das ist mal ein starkes Kompliment, danke dafür 8)

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 10:05
von BlackJack
@nezzcarth: Bei `crew()` ist das `divmod()` irgendwie fehl am Platz. Wenn man nur die Hälfte von dem Ergebnis braucht, sollte man auch nur diese Hälfte ausrechnen. ;-)

Code: Alles auswählen

    @property
    def crew(self):
        return self.tonnage // 10 + self.masts * 10

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 10:11
von nezzcarth
/me hat geschrieben:
nezzcarth hat geschrieben:Ich würde jetzt gerne ein Property wie z.B. 'crew' zusätzlich optional auch wie eine 'normale' Funktion mit Parametern aufrufen können.
Was sollen diese Parameter denn machen? Möchtest du unabhängig von einer konkreten Instanz Werte für tonnage und masts übergeben können?

Edit: Falls das gewünscht ist, dann ist BlackJacks Vorschlag die gewünschte Lösung.
Ja, das war gemeint. Mir war erst nach dem abschicken klar, dass da ein Beispiel nötig wäre, das ich dann noch reineditiert hatte.

Danke für eure Antworten :) Ich hatte ja irgendwie gehofft, dass das vielleicht noch leichter geht, also, dass man das irgendwie über
die Signatur regeln könnte und tatsächlich nur eine einzige Definition hat, oder so (aber wie das gehen soll, weiß ich auch nicht ;) )

Wie sieht es hiermit aus

Code: Alles auswählen

class Ship:
    def __init__(self, length, width, depth, masts, name="Nameless Ship"):
        self.length=length
        self.width=width
        self.depth=depth
        self.masts=masts
        self.name=name
    @staticmethod
    def _tonnage(length, width, depth):
        return (length * width * depth) / 3
    @property
    def tonnage(self):
        return self._tonnage(self.length, self.width, self.depth)
Kann/sollte man das so machen, oder eher nicht?


EDIT:
@Blackjack: Oh je, natürlich; manchmal fallen mir leider nur die kompliziertesten Lösungen ein... :oops:

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 10:19
von /me
Eine statische Methode mit einem Unterstrich als nur für interne Benutzung zu kennzeichnen kommt mir irgendwie merkwürdig vor. Irgendwie will mir dafür spontan kein Use Case einfallen.

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 10:21
von Hyperion
nezzcarth hat geschrieben: Kann/sollte man das so machen, oder eher nicht?
Da spricht prinzipiell nichts dagegen (außer, dass man den Underscore nach *hinten* setzen sollte. Denn vorne haben sie per Konvention die Bedeutung einer *internen* Methode, die nicht öffentlich verwendet werden sollte), aber dann hast Du natürlich einen eher hässlichen Methodenaufruf ;-)

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 12:17
von nezzcarth
Danke euch allen für die Hinweise. Den Unterstrich lasse ich bleiben; da hatte ich was falsch verstanden, bzw. interpretiert (z.B. bei entsprechenden Namen in collections.namedtuple).

Aus Interesse hatte ich mal versucht, das Verhalten, das mir vorschwebt zu basteln. Das sähe dann vielleicht so aus:

Code: Alles auswählen

class TestShip:
    def __init__(self, length, width, depth, masts, name="Nameless Ship"):
        self.length=length
        self.width=width
        self.depth=depth
        self.masts=masts
        self.name=name

    @staticmethod
    def tonnage(length, width, depth):
        return round((length*width*depth)/3)

    @staticmethod
    def capacity(tonnage):
        return round(tonnage*2/3)

    @staticmethod
    def crew(tonnage, masts):
        return tonnage//10 + masts*10
        
    def __getattr__(self, name):
        if name.startswith('get_'):
            _, method_name = name.split('_', maxsplit=1)
            method = getattr(self, method_name)
            parameters = []
            if not method:
                raise AttributeError
            for var in method.__code__.co_varnames:
                try:
                    parameters.append(self.__dict__[var])
                except KeyError:
                    t = getattr(self, 'get_'+ var)
                    parameters.append(t)
            return method(*parameters)

Code: Alles auswählen

>>> ship = TestShip(20,5,3,2)
>>> ship.print_stats()
Name: Nameless Ship
Länge: 20
Breite: 5
Tiefgang: 3
Masten: 2
Schiffsraum: 100
Frachtraum: 67
Mannschaft: 30
>>> ship.get_tonnage
100
>>> ship.tonnage(20,5,3)
100
Aber das ist vmtl. etwas sehr holperig und zusammengewürfelt... ;) Könnte man sowas auch 'in schön' machen, oder ist das an sich 'ne schlechte Idee? (u.a. wegen der komischen, konvetionalisierten Benennung).

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 12:26
von Hyperion
Wenn schon suchst Du ``@classmethod`` - denn welchen Sinn sollte hier eine static method haben? :? Du willst ja offenbar Berechnungen *ohne* Exemplar einer Klasse durchführen, oder nicht?

Generell halte ich nichts von so viel unnötiger Magie - zumal der eigentliche Vorteil von Properties hierdurch ad absurdum geführt werden ;-)

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 13:12
von BlackJack
@Hyperion: Ich schliesse mich da Hyperion (fast) an. Das sollte weder eine statische noch eine Klassenmethode sein, sondern einfach eine Funktion. Und vielleicht auch nicht `tonnage()` heissen sondern besser `volume()`, denn das ist es ja letztendlich was die Funktion berechnet. Ist ja auch nicht wirklich abhängig von einem Schiff.

Re: Properties gleichzeitig als (statische) Methoden verwend

Verfasst: Donnerstag 18. September 2014, 13:33
von nezzcarth
Ja, das ist schon richtig; wahrscheinlich sollte man auch erst einmal den " normalen" Kram richtig verstanden haben, bevor man mit solchen Spielereien beginnt. Ich war halt nur neugierig, wie man das wohl umsetzen könnte ;)

@BlackJack: Tonnage kommt nicht von mir (bzw. nur insofern, als dass ich es übersetzt habe) sondern von dem Spiel; dort wird der Wert in einer Gewichtseinheit (dem Äquivalent zu Tonnen) angegeben, nicht in einem Volumenmaß.