Kellerspeicher und umgekehrte Polnische Notation

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.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 23. November 2008, 19:53

Naja, Abstrakte Klassen sind ja auch eher wenig gebräuchlich in Python (übrigens würden die dann eher NotImplementedError werfen, statt still und leise mit pass zurückzukehren). Das ist in Java (Interfaces) und Scala (Traits) etwas anders, dort definiert so eine Abstrakte Klasse eine API und die konkreten Klassen implementieren das. Ermöglicht also nichts was nicht in Python auch so schon möglich ist, soll aber wohl etwas mehr Konsistenz sicherstellen (auch wenn ich davon selbst eigentlich nicht besonders überzeugt bin).

Python 3 bietet noch ein etwas anderes Konzept, sogenannte Abstract Base Classes (ABC), aber das werd ich jetzt auch nicht groß diskutieren, bei Interesse einfach in die Dokumentation gucken.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
DasIch
User
Beiträge: 2450
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sonntag 23. November 2008, 19:58

Ganz einfach ein Underscore signalisiert dass man das, was auch immer es ist, nicht verwenden oder verändern sollte aber es behindert nicht dabei sollte man es damit etwas anstellen.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Sonntag 23. November 2008, 20:09

@jonas

Diese Abstrakten Klassen waren nur ein Beispiel. Die Methoden mit Unterstrichen stellen einfach Dinge da, die man nicht verwenden sollte. Meistens sind das Dinge, die die Klasse intern braucht. Abstrakte Klassen finde ich sind da ein gutes Beispiel, da es bei dem Beispiel egal ist, ob du noch 5 andere Unterklassen von "AbstractCls" hast, da alle die grundlegenden Operation von "AbstractCls" benutzen. Und genau diese sind nur anderen Objekten, die damit umgehen sollen, bekannt. Insofern macht es keinen Sinn, "_do_sth_with_add" ohne Unterstrich zu schreiben, da die Methode sowieso keinem anderem Objekt bekannt ist und somit auch nicht dafür gedacht ist, von diesen verwendet zu werden. Der Unterstrich signalisiert also "Achtung, verwende mich nicht, wenn du nicht weißt, was du machst!". In dem "AbstractCls" Beispiel könnte zb jemand diese Methode verwenden, weil er denkt, jede Klasse hätte sie und sie gehört zur "Api", obwohl sie ja nur zur "Cls" gehört. ---> "NameError: ....".

Generell sind Unterstriche zum Zweck, den DasIch auch schon erläutert hat, gedacht. Das ist nur ein Anwendungsfall.

@Leonidas:

Die einzelnen Methoden müssen ja nicht einfach durchgehen. In Java ist das afaik ja zwingend, das man eine interface Methode nicht weiter spezifieren kann. Aber wenn 5 verschiedene Parser beispielsweise sowieso immer ~3 Methoden haben, die komplett gleich sind, unterscheidet sich das nicht vom normalen Einsatzzweck von Vererbung.

Statt dem NotImplementedError gibts auch das NotImplemented Singleton. Wäre wohl besser, das zurückzugeben. Zumindest meine ich, das sollte man auch machen (hab da irgendwas, was mit Dive into Python in Verbindung steht, im Hinterkopf).
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 23. November 2008, 20:22

str1442 hat geschrieben:Die einzelnen Methoden müssen ja nicht einfach durchgehen. In Java ist das afaik ja zwingend, das man eine interface Methode nicht weiter spezifieren kann. Aber wenn 5 verschiedene Parser beispielsweise sowieso immer ~3 Methoden haben, die komplett gleich sind, unterscheidet sich das nicht vom normalen Einsatzzweck von Vererbung.
Ja, natürlich, aber funktionen die implementiert werden sollten, sollten Ausnahmen werfen. Sonst kann man sie auch einfach nicht implementieren und sich wundern warum dann an seltsamen Stellen ``None`` auftaucht. Ich finde es ähnlich wie du auch wenig sinnvoll wenn man in Interfaces nicht auch teile der Implementation haben kann. In Scalas Traits ist das anders, dort kann man in den Traits schon eine Implementation haben aber wenn man abstrakte Methoden des Traits nicht implementiert, wird es nicht kompilieren. Sowas kann man eben mit normalen Klassen und NotImplementedError auch haben; allerdings zur Laufzeit.
str1442 hat geschrieben:Statt dem NotImplementedError gibts auch das NotImplemented Singleton. Wäre wohl besser, das zurückzugeben.
Wieso? Statt ``None`` wird man dann ``NotImplemented`` im Programm umherfliegen haben und man darf dann den Debugger anwerfen um festzustellen welche Funktion nicht implementiert wurde. Da finde ich Ausnahmen wesentlich besser, weil eben der letzte Frame im Traceback der Funktion entspricht, die man überschreiben muss.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Sonntag 23. November 2008, 20:35

Wieso? Statt ``None`` wird man dann ``NotImplemented`` im Programm umherfliegen haben und man darf dann den Debugger anwerfen um festzustellen welche Funktion nicht implementiert wurde. Da finde ich Ausnahmen wesentlich besser, weil eben der letzte Frame im Traceback der Funktion entspricht, die man überschreiben muss.
Wie gesagt, ich meine nur irgendwo mal was diesbezüglich gelesen zu haben. Ansonsten ist eine Ausnahme wohl besser geeignet, ja. Welchen Zweck hat aber NotImplemented, wenn man es sowieso nie zurückgeben sollte? Gibt ja dann eigentlich keinen Einsatzzweck mehr dafür.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 23. November 2008, 20:41

str1442 hat geschrieben:Welchen Zweck hat aber NotImplemented, wenn man es sowieso nie zurückgeben sollte? Gibt ja dann eigentlich keinen Einsatzzweck mehr dafür.
Ich muss gestehen dass ich NotImplemented auch noch nie verwendet habe, aber die Dokumetation sagt schon wozu es gut ist:
Python Dokumentation hat geschrieben:Numeric methods and rich comparison methods may return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.)
Ist also für ganz andere Sachen gedacht.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Sonntag 23. November 2008, 20:48

Stimmt, das war im Zusammenhang mit den __r*__ Methoden, zb __radd__. Auch wenn ich diese Methoden als "Rückfallmethoden" noch nie irgendwo gesehen hab, scheint NotImplemented nur dazu gut zu sein.

Das steht beim NotImplementedError:
This exception is derived from RuntimeError. In user defined base classes, abstract methods should raise this exception when they require derived classes to override the method.
Insofern scheint das ja geklärt zu sein :D
Qubit
User
Beiträge: 75
Registriert: Dienstag 7. Oktober 2008, 09:07

Montag 24. November 2008, 01:38

xdhwde hat geschrieben: theoretisch müsst ich ja nur noch ne raw_input methode machen für die werte und dann die methode pop and eval aufrufen oder? habt ihr noch verbesserungs vorschläge!
Das wäre eine Möglichkeit, zB so

Code: Alles auswählen

from math import *

class CalcUPN(object):
    
    DIGITS = '0123456789'
    SEPARATOR = ' '

    def __init__(self):
        self.result = 0.000
        self.clearHistory()

    def __repr__(self):
        return "%.3f" % self.result

    def __operation(self,op):
        op1 = str(self.__stack.pop())
        try:
            op2 = str(self.__stack.pop())
        except IndexError:
            op = op + '('
            op1 = op1 + ')'
            op2 = ''

        return eval(op2 + op + op1)

    def calc(self,user_input = None):
        self.__stack = []
        if not user_input:
            user_input = raw_input("calc: ").split(self.SEPARATOR)
        else: user_input = user_input.split(self.SEPARATOR)
        for data in user_input:
            self.__stack.append(float(data)) if data[0] in self.DIGITS else self.__stack.append(self.__operation(data))
        self.result = self.__stack[0]
        self.history.append(self.result)
        return self.result

    def clearHistory(self):
        self.history = []


>>> calc1 = CalcUPN()
>>> calc1.calc()
calc: 1 2 + 3 *
9.0
>>> calc1.calc()
calc: 1 2 + 3 4 + * 5 + 6 7 - -
27.0
>>> calc1.calc('4 3 - 2 /')
0.5
>>> calc1.calc()
calc: 1 2 + 3 * sin
0.41211848524175659
>>> calc1.calc()
calc: 1 2 + 3 * radians sin
0.15643446503974723
>>> calc1.result
0.15643446503974723
>>> calc1.history
[9.0, 27.0, 0.5, 0.41211848524175659, 0.15643446503974723]
>>> calc1
0.156
>>> 
DasIch
User
Beiträge: 2450
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Montag 24. November 2008, 02:32

@Qubit Vielleicht als Beispiel dafür wann man Klassen nicht benötigt. raw_input in einer Methode ist nicht unbedingt eine gute Idee und ansonsten macht calc die ganze arbeit.

Code: Alles auswählen

import operator

OPERATORS = {
    '+': operator.add,
    '-': operator.sub,
    '*': operator.mul,
    '**': operator.pow,
    '//': operator.truediv,
    '%': operator.mod
    }

class Stack(list):
    def __init__(self, *args):
        for arg in args:
            self.append(arg)

    def append(self, element):
        if element in OPERATORS:
            if len(self) < 2:
                raise ValueError('Not enough operands on the stack')
            list.append(self, OPERATORS[element](*(arg for arg in self.pop())))
        elif str(element).isdigit():
            list.append(self, element)
        else:
            raise TypeError('{0} is not an operator or digit'.format(element))

    def pop(self):
        return [list.pop(self) for dummy in xrange(2)]
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Montag 24. November 2008, 09:00

Code: Alles auswählen

            list.append(self, OPERATORS[element](*(arg for arg in self.pop()))) 
Oo

Warum nicht einfach self.pop() ?
BlackJack

Montag 24. November 2008, 10:00

Weil sich die Methode dann rekursiv selbst aufruft, statt die der Basisklasse auf zu rufen.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Montag 24. November 2008, 15:21

Ne ;)

Aber ich meinte auch eigentlich was anderes:

Code: Alles auswählen

            list.append(self, OPERATORS[element](*(arg for arg in self.pop()))) 
ersetzen durch

Code: Alles auswählen

            list.append(self, OPERATORS[element](*self.pop())) 
DasIch
User
Beiträge: 2450
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Montag 24. November 2008, 15:39

Arg, ich hatte vorher eine Konstruktion mit eval da hab ich dass benutzt um strings zu bekommen. Muss ich irgendwie übersehen haben.
Antworten