zugriff auf builtins

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
elactic
User
Beiträge: 18
Registriert: Mittwoch 16. Dezember 2009, 14:49

Hallo zusammen,
Ich habe versucht einen eigenen Datentypen für Stacks implementieren (zur Übung - über Sinn lässt sich sicher streiten).
Ich habe es schon mit einfach verketteten Elementen gebaut - keine Probleme.
Jetzt wollte ich das ganze nochmal mit Oython Standardlisten versuchen.
Ich wollte jetzt also für die methode pop() auf das schon definierte pop() für Listen zugreifen.
Bei einem anderen Projekt habe ich einmal eine Methode open() definiert und mit builtins.open() auf die vordefinierte Methode open() zugegriffen.
So ähnlich wollte ich das bei meinem Stack auch machen:

Code: Alles auswählen

import builtins
class Stack:
   def __init__(self):
        self.inhalt=[]
...
   def pop(self):
        self.inhalt.builtins.pop()
...
Die Fehlermeldung ist natürlich:

Code: Alles auswählen

AttributeError: 'list' object has no attribute 'builtins'
Könnt ihr mir sagen wo genau der Fehler liegt bzw wie ich es behebe?
Ich könnte natürlich mit Slicing arbeiten oder sowas wie del(a[-1]) benutzen, aber ich würde gern grundsätzlich wissen, wie ich auf vordefinierte Methoden zugreife, wenn ich ihren Name 'überschreibe'.
LG
elactic
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Code: Alles auswählen

class NewList(list):
    def pop(self):
        elem = list.pop(self)
        print elem
        return elem
Der Knackpunkt ist hier, daß du 'list.pop' eine Referenz auf das aktuelle Objekt explizit mitgeben mußt.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Erstmal heißt das Attribut `__builtins__` (mit den Unterstrichen). Zum Anderen ist `pop()` keine Builtin-Funktion, sondern eine Methode für Objekte vom (abgeleiteten) Typ `list`. Die wirst du also eh nicht über das Attribut erreichen. Und zu guter Letzt ist AFAIK nicht garantiert, dass dieses Attribut in einer Python-Implementierung vorhanden ist. Besser greifst du in solchen Fällen auf das Modul `__builtin__` (ohne "s" am Ende) zurück.
elactic
User
Beiträge: 18
Registriert: Mittwoch 16. Dezember 2009, 14:49

snafu hat geschrieben:Erstmal heißt das Attribut `__builtins__` (mit den Unterstrichen). Zum Anderen ist `pop()` keine Builtin-Funktion, sondern eine Methode für Objekte vom (abgeleiteten) Typ `list`. Die wirst du also eh nicht über das Attribut erreichen. Und zu guter Letzt ist AFAIK nicht garantiert, dass dieses Attribut in einer Python-Implementierung vorhanden ist. Besser greifst du in solchen Fällen auf das Modul `__builtin__` (ohne "s" am Ende) zurück.
__builtin__ ist Python 2, builtins ist Python 3 Soweit habe ich das zumindest verstanden und so hat es bisher auch funktioniert (Bsp builtins.open())
Ich habe vergessen zu sagen: Ich nutze Python 3!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.
elactic hat geschrieben:__builtin__ ist Python 2, builtins ist Python 3 Soweit habe ich das zumindest verstanden und so hat es bisher auch funktioniert (Bsp builtins.open())
Dein Problem hat aber nichts mit builtins zu tun. Lösungen wurden dir doch bereits angeboten. Entweder, du löst es mit Vererbung, wie von Pekh vorgeschlagen, oder es handelt sich um ganz einfachen Attributzugriff.
elactic hat geschrieben:ch habe vergessen zu sagen: Ich nutze Python 3!
Nun ist es nur noch eine Frage der Zeit, bis sich ein gewisser Benutzer mit einer bestimmten Umfrage wieder zu Wort melden wird ^^

Sebastian
Das Leben ist wie ein Tennisball.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

EyDu hat geschrieben:Dein Problem hat aber nichts mit builtins zu tun. Lösungen wurden dir doch bereits angeboten. Entweder, du löst es mit Vererbung, wie von Pekh vorgeschlagen, oder es handelt sich um ganz einfachen Attributzugriff.
Da ich das Gefühl habe, dass der OP auf dem Schlauch steht, schreibe ich mal aus, wass mit dem letzten Nebensatz gemeint ist:

Code: Alles auswählen

        self.inhalt.pop()
muss die Zeile in dem Code heißen.
elactic
User
Beiträge: 18
Registriert: Mittwoch 16. Dezember 2009, 14:49

mhh funktioniert...^^ Vielen Dank.
Aber verstanden hab ichs nicht!
Wenn ich pop() selber definiere und dann aufrufe, war ich davon ausgegangen, dass er genau diese Defnition aufruft (rekursiv).
Ich habe doch diese Methode überladen oder nicht?

Zum Thema Python 3:
Mein Dozent (selber Entwickler bei Python) hat mir empfohlen auf 3.x umzusteigen, aber im Hinterkopf zu behalten, dass viele Bibliotheken noch nicht übersetzt wurden -.-
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du bringst da was durcheinander. Du hast zwar eine Methode `pop()` definiert, aber du rufst in deiner eigenen Methode die Methode eines ganz anderen Objektes auf, welches an das Examplar deiner Klasse als Attribut gebunden wurde. Darauf hat deine Definition überhaupt keinen Einfluss.
elactic
User
Beiträge: 18
Registriert: Mittwoch 16. Dezember 2009, 14:49

Also so wie ich dich jetzt verstehe, funktioniert das pop() weil ich es als methode der Klasse list nutze.
Aber jetzt habe ich doch Stack von list abgeleitet. Wenn ich da jetzt ein pop() definiere, überschreibe ich doch die Definition von pop() in list.
Ich habe keine Ahnung wie list intern implementiert ist...
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Zunächst mal ist die Klasse in dem Code aus deinem Beispiel noch gar nicht abgeleitet. Aber selbst wenn man `class Stack(list)` definiert, dann ist ein `self.inhalt.pop()` immer noch etwas anderes als ein `self.pop()` (ich bin weiter beim eingangs genannten Code). Nur beim besagten `self.pop()` würde wieder deine selbst definierte Methode aufgerufen werden. Das willst du allerdings nicht, weil das Ganze sonst in endloser Rekursion endet. Also nimm `list.pop(self, ...)`, wie schon erklärt wurde, oder halt `self.inhalt.pop()`.
elactic
User
Beiträge: 18
Registriert: Mittwoch 16. Dezember 2009, 14:49

Ich war ein paar Tage nicht da, ich hoffe ich darf das hier nochmal aufwärmen...
snafu hat geschrieben:Zunächst mal ist die Klasse in dem Code aus deinem Beispiel noch gar nicht abgeleitet.
Oh hatte vergessen den neuen Code zu posten.

Code: Alles auswählen

class Stack(list):
    def __init__(self):
        self.inhalt=[]
    def empty(self):
        return len(self.inhalt)==0
    def push(self,item):
        self.inhalt.append(item)
    def pop(self):
        if not self.empty():
            self.inhalt.pop()
        else: raise Exception("Der Stack ist leer!")
    def top(self):
        if not self.empty():
            return self.inhalt[-1]
        else: raise Exception("Der Stack ist leer!")
    def __str__(self):
        return str(list(reversed(self.inhalt)))
snafu hat geschrieben:Aber selbst wenn man `class Stack(list)` definiert, dann ist ein `self.inhalt.pop()` immer noch etwas anderes als ein `self.pop()`
Weil self.pop() auf ein Stack-Objekt und self.inhalt.pop() auf ein Listen Objekt zeigt oder warum?
snafu hat geschrieben:Nur beim besagten `self.pop()` würde wieder deine selbst definierte Methode aufgerufen werden. Das willst du allerdings nicht, weil das Ganze sonst in endloser Rekursion endet.
Das war auch nicht mein Wunsch sondern meine Befürchtung ;)
snafu hat geschrieben: Also nimm `list.pop(self, ...)`, wie schon erklärt wurde, oder halt `self.inhalt.pop()`.
Ok hab ich gemacht, geht, habe ich denk ich verstanden.
Aber haben wir denn jetzt eigentlich was gewonnen, dadurch, dass wir von List geerbt haben? Wenn sich das pop() sowieso auf das Listen-Objekt (darf man das überhaupt sagen?) bezieht...
Wenn ich den Code genau wie oben, bloß ohne die Vererbung schreibe, funktioniert es genausogut...
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dir sollte klar sein, dass du da eine "Liste" hast, die als Attribut eine weitere Liste hat:

Code: Alles auswählen

In [1]: from stack import Stack

In [2]: s = Stack()

In [3]: s.append(1)

In [4]: s.append(2)

In [5]: s.empty()
Out[5]: True

In [6]: s[:]
Out[6]: [1, 2]

In [7]: str(s)
Out[7]: '[]'
Wenn das nicht gewollt ist - was ich hoffe - solltest du dich an Pekhs Code halten. Oder noch besser `super` nutzen: http://docs.python.org/library/functions.html#super

Edit: Hab den Code mal ueberarbeitet:

Code: Alles auswählen

class Stack(list):
    def empty(self):
        return len(self) == 0
    def push(self, item):
        self.append(item)
    def pop(self, index=-1):
        if not self.empty():
            super(Stack, self).pop(index)
        else:
            raise Exception("Der Stack ist leer!")
    def top(self):
        if not self.empty():
            return self[-1]
        else:
            raise Exception("Der Stack ist leer!")
    def __str__(self):
        return str(list(reversed(self)))
Aber den Stack von `list` abzuleiten hat ein paar Probleme, v.a. dass du dir ein Interface einhandelst, dass du eigentlich gar nicht haben willst in dem Fall z.b. `__setitem__`.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

elactic hat geschrieben:Aber haben wir denn jetzt eigentlich was gewonnen, dadurch, dass wir von List geerbt haben? Wenn sich das pop() sowieso auf das Listen-Objekt (darf man das überhaupt sagen?) bezieht...
In deinem Anwendungsfall wohl eher nicht. Man nutzt Vererbung in aller Regel, um eine bereits vorhandene Implementierung zu erweitern. Vererbung meint dabei eine Ist-Beziehung.

Um anhand deines Beispiels mal konkreter zu werden: Man kann dem in Python vorhandenem Objekt `list` zu Übungszwecken durchaus eine Methode `is_empty()` spendieren (wobei das letztlich zu einem `return (not self)` gekürzt werden kann). Dein Stack hingegen sollte eher nicht von `list` erben, weil ein Stack IMHO zwar einen Container, aber keine Liste darstellt. Eine Eigenschaft, die man (auch außerhalb von Python) von einer Liste erwarten würde, wäre beispielsweise, dass ich aus 10 vorhandenen Elementen das dritte Element herausnehmen kann. Das Prinzip geht bei einem Stack natürlich nicht und es würde deshalb eher verwirren, wenn dein Stack-Objekt noch zusätzlich etwas wie das von der Liste übernommene `append()` im Interface hätte. Von daher ist es in meinen Augen schon ganz sinnvoll, die Lagerung der Objekte intern als Instanzattribut zu implementieren (etwa: `._data`) und für Dinge wie `pop()` halt mehr oder weniger simple Forwarding-Methoden zu schreiben. Im Grunde machst du das ja auch bereits so. Nimm halt am Besten noch die Vererbung raus.

Übrigens ist auf der deutschen Wikipedia-Seite zu Python die Implementierung eines Stacks zu finden, welche Closures benutzt:

Code: Alles auswählen

def stack():
  l = []
  def pop(): return l.pop()
  def push(element): l.append(element)
  def isempty(): return len(l) == 0
  return pop, push, isempty
 
POP, PUSH, ISEMPTY = stack()
Mag nicht ganz so praxistauglich in der Anwendung sein, aber ich wollt's bei der Gelegenheit mal zeigen. :)
Antworten