Klasse erweitern

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
toy
User
Beiträge: 3
Registriert: Dienstag 5. April 2011, 18:11

Hallo,

ist es Möglich eine Klasse um Methoden aus einer anderen Klasse zu erweitern/ändern?

Bsp.:

Ordnerstruktur:
main.py
module/__init__.py
module/minusrechnen.py

main.py:

Code: Alles auswählen

import module

class a(object):
 def rechnen(self,wert1,wert2):
  return wert1+wert2

meinobjekt=a()
module/__init__.py:

Code: Alles auswählen

import minusrechnen.py
module minusrechnen.py:

Code: Alles auswählen

 ...
 def rechnen(self,wert1,wert2):
  return wert1-wert2
...
Ich möchte erreichen, dass ich mit meinobjekt.rechnen minus anstatt plus rechne.

Ich höffe ihr versteht was ich meine...

MFG

Toy
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Entweder ableiten oder die Nutzung von der Build-In Funktion "setattr"
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
toy
User
Beiträge: 3
Registriert: Dienstag 5. April 2011, 18:11

Ich kann mir nicht vorstellen, wie das gehen soll/kann. Es ist wichtig, dass die Klasse in main.py initialisiert wird.

mfg

toy
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also echte Mixins gibt es afaik in Python nicht.

Du könntest ja einfach eine Funktion als Parameter übergeben:

Code: Alles auswählen

def rechnen(self, func, *params):
    return func(params) # oder was auch immer
Damit kannst Du zur Laufzeit an der Klasse was drehen.

Aber ehrlich gesagt habe ich Dein einleitendes Posting nicht exakt kapiert. Alleine durch den import von "minusrechnen" (was ja eigentlich ein Modul ist und keine Funktion / Klasse?) soll sich dann in der Klasse "a" etwas an der Methode "rechnen" ändern?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
deets

Hyperion hat geschrieben:Also echte Mixins gibt es afaik in Python nicht.
Wie nennt man denn die Mixins dann, die Python so kennt?

An den OP, das hier koennte helfen:

Code: Alles auswählen


class Foo(object):


    def bar(self):
        print "bar"


def foo(self):
    print "foo"

Foo.bar = foo

f = Foo()
f.bar()
LanX
User
Beiträge: 92
Registriert: Samstag 20. Februar 2010, 12:46

Ich vermute mal ganz stark dass es sich hier um ein xy-Problem handelt und einfache Vererbung die sauberste Lösung wäre.

Ansonsten in Perl kann man per AUTOLOAD den Auffruf einer fehlende Methode abfangen, und stattdessen eine andere aufrufen (oder für künftige Aufrufe importieren).

Ich denke mit __getattr__ erreicht man das gleiche in Python und könnte so auch dynamisch Mixins realisieren.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Einige Programmiersprachen, z.B. Ruby oder Smalltalk erlauben es, Klassen (sowohl eigene als auch eingebaute) zu "öffnen" und dort weitere Methoden hinzuzufügen. In C# oder Scala sieht es so aus, als wenn es auch ginge, dort ist es aber jeweils ein syntaktischer Trick, in der Praxis gut genug ist. Python kann das generell nicht. Die Menge der Methoden von allen eingebauten Klassen ist hier unveränderlich.

Da Python sein Metamodell offenlegt, also in der Sprache selbst definiert, wie die Sprache hier funktioniert, kann man bei eigenen Klassen das in dem Attribut __dict__ gespeicherte Dictionary, welches die Methoden enthält einfach ändern. Eine Funktion, die man in so ein Dictionary hineintut, wird automagisch zu einer Methode, wenn man sie wieder erfragt. Die Implementierung hier offen zu legen macht Python sehr flexibel, aber verhindert oder erschwert bestimmte Optimierungen.

Kurz um, man kann folgendes machen:

Code: Alles auswählen

class A:
    pass
def m(self, x):
    return x * x
A.__dict__[m.__name__] = m
a = A()
print A.m(a, 4)
print a.m(4)
Die letzte Zeile - der Aufruf einer Methode - ist nämlich nur syntaktischer Zucker für die Zeile darüber.

Stefan

Update: Dictionary-Zugriff korrigiert
Zuletzt geändert von sma am Mittwoch 6. April 2011, 16:28, insgesamt 1-mal geändert.
BlackJack

@sma: Das düfte so nicht funktionieren. `__dict__` sollte schon ein Dictionary sein und keine Funktion. Ausserdem besteht hier keine Notwendigkeit auf das "magische" `__dict__` zuzugreifen -- man kann doch einfach das Attribut auf der Klasse setzen:

Code: Alles auswählen

In [210]: class A(object): pass
   .....: 

In [211]: def m(self, x):
   .....:     return x * x
   .....: 

In [212]: A.m = m

In [213]: a = A()

In [214]: a.m(42)
Out[214]: 1764
LanX
User
Beiträge: 92
Registriert: Samstag 20. Februar 2010, 12:46

@sma:

Code: Alles auswählen

TypeError: __dict__ must be a dictionary object

---update
meintest du vielleicht:

Code: Alles auswählen

A.__dict__["m"] = m
?
update---

@all:
Frage: kann man nicht dynamisch ändern von wem eine Klasse erbt? Dass wäre doch am elegantesten.

Für echte Mixins müsste man wohl an die Metaklassen ran.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

LanX hat geschrieben:@sma: meintest du vielleicht: [...]
Peinlich, peinlich, ja natürlich. Habe ich korrigiert.
LanX hat geschrieben:Für echte Mixins müsste man wohl an die Metaklassen ran.
Man kann in Python auch __bases__ manipulieren, das ist die Liste oder Oberklassen, zu denen Klassen gehören können, die die Rolle von Mixins spielen:

Code: Alles auswählen

class A:
    def m(self): return 42
class B:
    pass
B.__bases__ += (A,)
print B().m()
Man beachte, dass dies old-style-Klassen von Python 1.x oder Python 2.x sind. Mit newstyle-Klassen von Python 2.x oder Python 3.x ist das nicht ganz so simpel.

Stefan
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

sma hat geschrieben: Man beachte, dass dies old-style-Klassen von Python 1.x oder Python 2.x sind. Mit newstyle-Klassen von Python 2.x oder Python 3.x ist das nicht ganz so simpel.
Dann vergisst man diese Idee doch besser wieder, da old-style Klassen i.d.R. der Vergangenheit angehören. Oder?!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

deets hat geschrieben:
Hyperion hat geschrieben:Also echte Mixins gibt es afaik in Python nicht.
Wie nennt man denn die Mixins dann, die Python so kennt?
Ja, welche sind denn das so?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Hyperion Ich vermute mal er meint sowas:

Code: Alles auswählen

class Widget(BaseWidget, Pack, Place, Grid):
    pass
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
deets

Genau das meinte, die multiple-inheritance in Python wird auch gerne als Mixins bezeichnet, siehe die zB die stdlib-Doku:

http://docs.python.org/library/socketserver.html

Inzwischen habe ich Wikipedia zu Rate gezogen - die offizielle Version schein wirklich das nachtraegliche aufbohren von Klassen zu meinen.

Insofern ist die Python-Lingo dann wohl falsch.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ja ok, ist wohl wirklich ein Problem der exakten Definition! Ich hatte es nur von der Ruby-Seite im Kopf, wo Mixins explizit als Unterschied zu Pythons Verfachvererbung genannt werden.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
LanX
User
Beiträge: 92
Registriert: Samstag 20. Februar 2010, 12:46

Hyperion hat geschrieben:Ja ok, ist wohl wirklich ein Problem der exakten Definition! Ich hatte es nur von der Ruby-Seite im Kopf, wo Mixins explizit als Unterschied zu Pythons Verfachvererbung genannt werden.
AFAIK:

Ruby realisiert MixIns durch include von Modulen in eine Klasse.
[strike]D.h. die Referenzen auf die Methoden werden dabei statisch umkopiert[/strike] ¹, wie es in diesem Thread schon gezeigt wurde.

Das geht auch elegant, weil Ruby nicht erzwingt dass Module in einem eigenen File leben.

Bei Vererbung hat man hingegen einen dynamischen Suchpfad für "fehlende" Methoden. Hat es die eigene Klasse nicht, sucht man in der (ersten!) Superklasse, hat die es nicht sucht man in deren Superklasse. Ist man oben erfolglos angekommen, schaut man (bei Mehrfachvererbung!) unten ob es im Suchpfad eine weitere Superklassen gibt, und das ganze fängt von neuem an. Das führt zu konzeptionell ziemlich schwer zu beherrschenden Problemen wie dem Diamantfall, wo zwei Superklassen eine gemeinsame Superklasse haben.

Inwieweit (Modul-) import in Python das gleiche leisten können wie in Ruby weiß ich jetzt nicht, es sollte aber ansonsten kein Problem sein den betreffenden Klassen eine Methode namens import zu designen die genau das Mixin-Schema von Ruby nachstellt (wie gezeigt über die Dicts iterieren und umkopieren)

dann ruft man so was auf wie Klasse.import(Superklasse)

Will man hingegen weiterhin eine dynamische Auflösung der Methode, könnte man z.B. auch __getattr__ überschreiben.

Und das sich Python seiner Metaklassen rühmt, sollte es eigentlich für beide Konzepte auch schon konkrete Umsetzungen geben.

UPDATE:

[1] so ich habe jetzt schmarrn erzählt, tatsächlich werden die Mixin-Methoden bei einem include nicht statisch umkopiert sondern das Modul wird dynamisch verlinkt. Sprich wird dem Modul später eine neue Methode hinzugefügt, ist sie dynamisch durch den Suchpfad sichtbar.
Antworten