ubuntuusers.de

Python, self an Funktion übergeben

Status: Ungelöst | Ubuntu-Version: Kubuntu 11.04 (Natty Narwhal)
Antworten |

Inkane

Anmeldungsdatum:
17. Oktober 2010

Beiträge: 306

Hallo,

ist es möglich, bei folgenden Code erreichen, dass foo nicht exa übergeben werden muss? Also, dass automatisch self übergeben wird.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
>>> class Example:
...     def __init__(self, foo):
...         self.foo = foo
...         self.bar = 42
... 
>>> def myfoo(self):
...     print(self.bar)
... 
>>> exa = Example(myfoo)
>>> exa.foo()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: myfoo() takes exactly 1 argument (0 given)
>>> exa.foo(exa)
42

Gruß,
Inkane

EnTeQuAk Team-Icon

Avatar von EnTeQuAk

Anmeldungsdatum:
17. Mai 2006

Beiträge: 3289

Hey, ich versuche gerade zu verstehen warum du das machen möchtest, hast du hier mal ein besseres Beispiel?

Was du gegebenenfalls suchst is functools.partial

Gruß, Christopher.

Riddle

Avatar von Riddle

Anmeldungsdatum:
27. Juli 2007

Beiträge: 201

Wohnort: 127.0.0.1

Soll myfoo zur Klasse gehören oder global sein?

Bei ersterem passt die Einrückung nicht, bei zweitem macht self als Bezeichner keinen Sinn.

Inkane

(Themenstarter)

Anmeldungsdatum:
17. Oktober 2010

Beiträge: 306

Gut, hier mal das bessere Beispiel, wenn es für das eigentliche Problem eine bessere Lösung gibt ist es natürlich umso besser.

Für den Programmierwettbewerb des freienMagazins schreibe ich gerade einen Bot, welcher unter anderem auch eine AI Klasse hat. Diese enthält unter anderem folgenden Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class BaseAI:

        def __init__(self, offer=raise_not_implemented,
                     accept=raise_not_implemented):
            self.runden = 1
            self.runde = 1
            self.punkte = 0
            self.punkte_gesamt = 0
            self.accepted = True
            self.data_table = dict()
            if offer:
                self.offer = offer
            if accept:
                self.accept = accept

offer und accept sind die Methoden, welche dazu dienen, ein Angebot abzugeben bzw. zu überprüfen. Über sie wird also das eigentliche Verhalten des Bots gesteuert. raise_not_implemented ist eine Funktion, die macht, was ihr Name sagt.

Um das Verhalten der Bots zu testen erstelle ich mehrere Bots mit unterschiedlichen Strategien und lasse sie gegeneinander antreten. Bisher hatte ich für jede Strategie eine eigene Klasse erstellt, welche von BaseAI erbt. Dies hat mit OOP meiner Meinung nach aber nicht mehr wirklich viel zu tun, da von jeder dieser Unterklassen immer nur ein einziges Objekt erstellt wird. Deswegen wollte ich jetzt eigentlich eine Klasse, von welcher mehrere Objekte mit variablen Strategien erzeugt werden können. Für bessere Vorschläge bin ich natürlich offen.

Riddle schrieb:

Soll myfoo zur Klasse gehören oder global sein?

myfoo soll global sein.

Bei ersterem passt die Einrückung nicht, bei zweitem macht self als Bezeichner keinen Sinn.

Naja, ich hätte es auch Schnitzel oder Blumentopf nennen können, das ist bei solch aussagekräftigen Beispielen dann eh egal. Mit self wollte ich andeuten, dass myfoo später ein Objekt als Eingabe erwartet. self ist in Python nun mal nur eine Konvention und kein Keyword.

Riddle

Avatar von Riddle

Anmeldungsdatum:
27. Juli 2007

Beiträge: 201

Wohnort: 127.0.0.1

Naja, Konvention ist aber eher, dass self nur innerhalb der Klasse für objektabhängige Methoden verwendet wird und nicht außerhalb, daher wollte ich einmal nachfragen (Beispiele die man leicht unterschiedlich deuten kann sind schwer zu verstehen).

Ich finde ja eigentlich, dass Vererbung gerade eines der Sachen ist, die Objektorientierung ausmacht, aber meinetwegen. Mit closures könnte man das eventuell hinbiegen, also etwas der Art

1
2
3
4
5
6
def strategy(bot):
    def offer():
        ...
    def accept():
        ...
    return offer, accept

und

1
2
3
4
5
6
7
8
class BaseAI:
        def __init__(self, strategy):
            ...
            offer, accept = strategy(self)
            if offer:
                self.offer = offer
            if accept:
                self.accept = accept

damit wäre der Bot dann eigentlich in offer und accept nutzbar ohne dass man das global machen oder übergeben müsste.

Ist aber alles ungetestet und ich möchte mich nicht dafür verbürgen.

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Riddle hat es ja eigentlich schon angedeutet, wie man es machen kann. Ob nun Funktion oder Klasse für eine spezielle AI können wir hier schlecht beurteilen. Ich hatte mal im Python-Forum einen kleinen Dungeon-Crawler angefangen, der Klassen nutzt¹. Da ich mir dort teilweise Zustände merken wollte usw. fand ich Klassen gegenüber einer Funktion sinnvoller.

¹Achtung: Einige Sachen da drin sind schlecht. Speziell das Default-Handling mittels global - k.A., was mich da geritten hat!

Zu Deinem Handling: Eine Funktion raise_not_implemented finde ich unschön! Werfe den Fehler doch in der __init__-Methode der AI-Klasse! Sinnvoller wäre es ggf. auch, einfach die Parameter offer und accept nicht als optional zu deklarieren. Dann würde es ja bei falscher Verwendung einen TypeError hageln!

Die Klasse sollte man bei Deiner Idee auch umbenennen: BaseAI impliziert ja, dass es spezialisierte AIs geben kann / soll. Genau das willst Du ja eigentlich nicht.

Inkane

(Themenstarter)

Anmeldungsdatum:
17. Oktober 2010

Beiträge: 306

Riddle schrieb: Mit closures könnte man das eventuell hinbiegen, also etwas der Art

...

Ja, danke, mit Closures funktioniert das ganz gut.

Lysander schrieb:

Ich hatte mal im Python-Forum einen kleinen Dungeon-Crawler angefangen, der Klassen nutzt¹. Da ich mir dort teilweise Zustände merken wollte usw. fand ich Klassen gegenüber einer Funktion sinnvoller.

Das schau ich mir dann mal an

Zu Deinem Handling: Eine Funktion raise_not_implemented finde ich unschön! Werfe den Fehler doch in der __init__-Methode der AI-Klasse! Sinnvoller wäre es ggf. auch, einfach die Parameter offer und accept nicht als optional zu deklarieren. Dann würde es ja bei falscher Verwendung einen TypeError hageln!

Das ist noch nicht fertig ausgearbeitet, wird so aber bestimmt nicht bleiben.

Die Klasse sollte man bei Deiner Idee auch umbenennen: BaseAI impliziert ja, dass es spezialisierte AIs geben kann / soll. Genau das willst Du ja eigentlich nicht.

Ja, das stammt noch aus der Zeit, wo jede Strategie als eigene AI-Klasse umgesetzt wurde.

Noch kurz zu Klasse vs. Funktionen: Im gesamten Programm wird nur ein einziges AI-Objekt erzeugt, die Strategie wird via Kommandozeilenargument gewählt und unter Umständen während des Spiels gewechselt. Da erschien es mir dann etwas sinnvoller, das ganze mit Funktionen zu lösen.

Nochmals Danke an alle.
Inkane

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Inkane schrieb:

Noch kurz zu Klasse vs. Funktionen: Im gesamten Programm wird nur ein einziges AI-Objekt erzeugt, die Strategie wird via Kommandozeilenargument gewählt und unter Umständen während des Spiels gewechselt. Da erschien es mir dann etwas sinnvoller, das ganze mit Funktionen zu lösen.

Das Argument mit dem einem Objekt kann man so nicht gelten lassen! Klassen "rentieren" sich ja nicht abhängig von der Anzahl späterer Objekte, sondern abhängig von "inneren Zuständen" oder allgemeiner der "Sinnhaftigkeit", Daten und Methoden zu kombinieren. Wenn Du Dir mal mein Beispiel anguckst, wirst Du schnell sehen, dass sich viele Sachen mit einfachen Funktionen nicht machen lassen, da ich mir Zustände merken muss.

Kinch

Anmeldungsdatum:
6. Oktober 2007

Beiträge: 1261

Nur zur vollständigkeit für die ursprüngliche Problemstellung:

Wenn du implizit Variablen übergeben willst, wäre das — bereits genannte — Currying eine Möglichkeit:

import functools

class Example:
  def __init__(self,foo):
    self.foo = functools.partial(foo,self)
    self.bar = 42


def myfoo(obj):
  print(obj.bar)

example = Example(myfoo)
example.foo()
Antworten |