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.
Kellerspeicher und umgekehrte Polnische Notation
@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).
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).
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
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: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.
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.str1442 hat geschrieben:Statt dem NotImplementedError gibts auch das NotImplemented Singleton. Wäre wohl besser, das zurückzugeben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
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.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.
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Ich muss gestehen dass ich NotImplemented auch noch nie verwendet habe, aber die Dokumetation sagt schon wozu es gut ist: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.
Ist also für ganz andere Sachen gedacht.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.)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
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:
Das steht beim NotImplementedError:
Insofern scheint das ja geklärt zu seinThis 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.
Das wäre eine Möglichkeit, zB soxdhwde 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!
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
>>>
@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)]
Code: Alles auswählen
list.append(self, OPERATORS[element](*(arg for arg in self.pop())))
Warum nicht einfach self.pop() ?
Weil sich die Methode dann rekursiv selbst aufruft, statt die der Basisklasse auf zu rufen.
Ne
Aber ich meinte auch eigentlich was anderes:
ersetzen durch
Aber ich meinte auch eigentlich was anderes:
Code: Alles auswählen
list.append(self, OPERATORS[element](*(arg for arg in self.pop())))
Code: Alles auswählen
list.append(self, OPERATORS[element](*self.pop()))