Klassenmethoden zur Laufzeit definieren

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
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Hallo,

ich würde gerne soetwas wie unten programmieren, geht aber nicht. Das einzige, was ich geschaft habe ist, Funktionen im Konstruktor zu definieren und danach einem Klassenattribut zuzuweisen. Dann allerdings muss ich der Methode das Objekt selbst explizit übergeben: x.t(x). Das ist natürlich nicht schön.

Code: Alles auswählen

class X(object):
	__metaclass__ = meta
	x = 0

	if x:
		def t(self):
			print "A"
	else:
		def t(self):
			print "B"

x = X(0)
x.t() # => "B"
x = X(1)
x.t() => "A"
Falls jemand einen Tipp hat wäre ich dankbar.

Gruß
Matthias
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Vielleicht liegt es nur an dem einfachen Beispiel, aber für so etwas verwendet man normalerweise Vererbung.
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Es liegt am einfachen Beispiel ;)
BlackJack

Oder in diesem Beispiel alternativ eine Factory-Funktion die eines von zwei Objekten zurückgibt.

Oder man übergibt 'A' oder 'B' im Konstruktor und gibt dass dann entsprechend aus.


Da das Beispiel aber viel zu einfach ist, kann man das nicht sagen. Welches Problem soll denn gelöst werden? Und bitte das Problem nicht in Form einer Lösung beschreiben, sondern wirklich das *Problem* selbst.
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Ich will unterschiedliche Dinge in einer Klasse kapseln, unter möglichst hoher Wahrung von Transparenz. Das bedeutet, dass mit "isinstance", "instanceof" & co. keine Unterscheidung möglich sein soll.

Aus diesen Gründen scheiden Vererbung und Factories aus.

Deshalb zurück zur problembeschreibenden Fragestellung:
Kann man "Klassenmethoden zur Laufzeit definieren"?

Was ich bisher hinbekommen habe ist:

Code: Alles auswählen

class X(object):
    define __init__(self, x):
        if x:
            def t(self):
                print "A"
        else:
            def t(self):
                print "B"
       self.t = t

y = X(1)
y.t(y) #=> A
y = X(0)
y.t(y) #=> B
Wie man sieht, muss dann aber das Objekt als Instanz übergeben werden.
Vielleicht kann man ja eine Funktion als Objektfunktion registrieren?
BlackJack

Du versuchst da etwas zu machen was unpythonisch ist -- Stichwort "duck typing". Der Typ sollte egal sein, wichtig ist das Verhalten des Objekts.
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Was meine Frage leider nicht beantwortet.

Im übrigen ist eine solche Implementierung selbstverständlich pythonic. Denn meine Klasse sieht nicht nur aus wie eine Ente, quackt wie eine Ente, sondern da steht sogar noch "Ente" drauf".
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

sondern da steht sogar noch "Ente" drauf".
das macht es ja eben unpythonic.
duck-typing ist wenn Hund drauf stehen würde und der sich wie die Ente verhält ;)

Ich weiß, das hilft bei deinem Problem nicht weiter, aber oft ist es besser nochmal drüber nachzudenken wie wichtig das mit dem gleichen "aussehen" deiner instanzen ist
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Mir ist klar, was "Duck typing" ausmacht. Deshalb nochmal:

Meine Klasse ist konform zu dem Paradigma, da die Menge aller Objekte, die sich wie Enten verhalten, eine Obermenge der Objekte ist, die sich wie Enten verhalten und bei denen zusätzlich "Ente" draufsteht.
=> Wenn man auf der Obermenge pythonic arbeiten kann, kann man das folglich auch mit der Untermenge. Das Ganze verursacht erst Probleme wenn man auf der Obermenge operiert und ein Kriterium der Untermenge nutzt - das ist der vorher angesprochene Kontext.

Im übrigen bleibt es jedem selbst überlassen, ob er pythonic programmiert oder nicht. Ich brauche das zum Beisipiel, um Geräte wie Achsen, Roboter, etc. über .dlls, .so oder sockets einzubinden. Denen kann ich noch so oft sagen, "quacke" wie eine Ente. Sie tuns nicht ;). Was an dieser Stelle erreicht werden soll, ist eine absolute Trennung zwischen einer Steuerungsarchitektur und den angeschlossenen Geräten. Und dazu gehört auch, dass ein Anwendungsprogrammierer "unpythonic" programmieren könnte. Ich kann diesen Fall nicht ausschließen und auch nicht jeden Programmierer zu einem Kurs in "duck typing" schicken.

Jedes Paradigma stößt an Grenzen, wenn mit anderen Systemen interagiert werden muss.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

BlackJack

Na dann korrigiere ich erstmal Deine Frage: "Ist es möglich Instanzmethoden zu einem Objekt hinzuzufügen?", denn an die Klasse möchtest Du die ja nicht binden.

Die Antwort ist: "Ja." :twisted:

Und zu Deinem Problem dass Du beschreibst: Das hört sich nach einem Fall für Komposition an.

Das mit den Unter- und Obermengen ist nett, aber das unpythonische ist die Verwendung von `isinstance()`. Das muss ja wohl irgendwo verwendet werden um diesen ganz bestimmten Typ zu testen, ansonsten gäb's das Problem nicht und man könnte auch den quakenden Hund benutzen.

Wieso kann man den Programmierern nicht die idiomatische Verwendung einer Programmiersprache beibringen? Wenn Du eine Sprache willst, in der man sich nur sehr schwer in den Fuss schiessen kann, bist Du bei Python falsch und solltest lieber eine "discipline & bondage"-Sprache wie Java verwenden. Von einem Python-Programmierer wird im allgemeinen erwartet, dass er weiss was er da tut. Das ist der Preis den man für die Flexibilität und Dynamik zahlen muss.
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

BlackJack hat geschrieben:Na dann korrigiere ich erstmal Deine Frage: "Ist es möglich Instanzmethoden zu einem Objekt hinzuzufügen?", denn an die Klasse möchtest Du die ja nicht binden.
der Punkt geht an Dich :)
BlackJack hat geschrieben: Das mit den Unter- und Obermengen ist nett, aber das unpythonische ist die Verwendung von `isinstance()`. Das muss ja wohl irgendwo verwendet werden um diesen ganz bestimmten Typ zu testen, ansonsten gäb's das Problem nicht und man könnte auch den quakenden Hund benutzen.
Der Punkt nicht. Ich habe nie behauptet, dass ich "instanceof" _verwende_, sondern dass es _möglich_ sein soll. Somit _kann_ das Problem bestehen, muss aber nicht. Wenn es entsteht, soll es transparent sein.

Wieso bietet Python eigentlich "unpythonische" Funktionen an? Kennen die Entwickler nicht die idiomatische Verwendung ihrer Sprache? Sollte man vielleicht van Russum zur Nachschulung schicken?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wozu das immer auch gut ist... Wie wäre es damit:

Code: Alles auswählen

class X(object):
    def __init__(self, mode):
        if mode == 0:
            self.t = self.t_A
        else:
            self.t = self.t_B

    def t_A(self):
        print "A"

    def t_B(self):
        print "B"

x = X(0)
x.t() # => "B"

x = X(1)
x.t() # => "A"

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Dill hat geschrieben:kennst du die artikel-serie:

http://www.onlamp.com/pub/a/python/2003 ... asses.html
Besten Dank, das war, was ich wollte :)
BlackJack

hiasl hat geschrieben:
BlackJack hat geschrieben: Das mit den Unter- und Obermengen ist nett, aber das unpythonische ist die Verwendung von `isinstance()`. Das muss ja wohl irgendwo verwendet werden um diesen ganz bestimmten Typ zu testen, ansonsten gäb's das Problem nicht und man könnte auch den quakenden Hund benutzen.
Der Punkt nicht. Ich habe nie behauptet, dass ich "instanceof" _verwende_, sondern dass es _möglich_ sein soll.
Also wird es doch irgendwo verwendet, wäre dem nicht so, löst Du ein Problem das es nicht gibt.
Wieso bietet Python eigentlich "unpythonische" Funktionen an? Kennen die Entwickler nicht die idiomatische Verwendung ihrer Sprache?
Och bitte. Man kann so ziemlich jedes Feature zweckentfremden. "Pythonische" Verwendung von Instanzmethoden, die zur Laufzeit einem Objekt hinzugefügt werden, wäre zum Beispiel einem Hund, der nicht quaken kann, eine solche Methode dynamisch zu verpassen, damit er irgendwo als Ente durchgeht und nicht damit dort dann ein `instanceof(obj, Ente)` funktioniert.

Idiomatische Verwendung bedeutet ja gerade, dass es nicht von der Sprache so erzwungen wird, sondern dass etwas in der Regel so formuliert wird, weil es der Philosophie der Sprache entspricht. Die meisten Sachen kann man auch gar nicht erzwingen, weil sie formal gar nicht erfasst werden. Selbst bei statischeren Sprachen geht das nicht.
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

Für den Fall, dass jemand ein ähnliches Problem hat, hier eine funktionierende Lösung:

Code: Alles auswählen

from new import instancemethod

class X(object):
	def __init__(self, x):
		if x:
			def t(self):
				print "A"
		else:
			def t(self):
				print "B"
		self.__setattr__('t', instancemethod(t, self, X))

X(1).t()
X(0).t()
@BlackJack: Die Diskussion führt zu keinem Ergebnis - weder für Dich noch für mich. Trotzdem Danke und noch einen schönen Tag :)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Warum nicht so:

Code: Alles auswählen

class X(object):
    def __init__(self, x):
        if x:
            def t():
                print "A"

        else:
            def t():
                print "B"
        self.t = t

X(1).t()
X(0).t()
EDIT: Ah, ok... wenn man zugriff auf self haben möchte, geht's so nicht ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

BlackJack hat geschrieben:Oder in diesem Beispiel alternativ eine Factory-Funktion die eines von zwei Objekten zurückgibt.
Sorry wenn ich hier so reinplatze. Was sind Factory-Funktion? :? Sowas wie Closures?
hiasl
User
Beiträge: 31
Registriert: Donnerstag 20. April 2006, 13:36

sape hat geschrieben:
BlackJack hat geschrieben:Oder in diesem Beispiel alternativ eine Factory-Funktion die eines von zwei Objekten zurückgibt.
Sorry wenn ich hier so reinplatze. Was sind Factory-Funktion? :? Sowas wie Closures?
Eine Factoryfunktion instantiiert ganz einfach in Abhänigkeit von Parametern Objekte von nunterschiedlichen Klassen. Damit hast Du einen zentralen Objekterzeuger und musst selbst nicht immer Fallunterscheidungen implementieren.

Gern zitiertes Beispiel:
Du kannst für jede Pizza eine eigene Klasse schreiben.
Den Pizzabäcker implementierst Du als Factory, der bei Bestellung der Pizza in Abhängigkeit des Pizzanamens die richtige Pizza erzeugt.
Antworten