OO-Prinzipien und Python

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.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Leonidas hat geschrieben:Argh, meine Erklärung ist irgendwie unklar.. mal sehen, vielleicht erklärt es jemand besser.
Ich probiere es mal. :)

Code: Alles auswählen

foo.bar()
Ist die abkürzende Schreibweise von:

Code: Alles auswählen

getattr(foo, "bar")()
Dabei ist egal ob bar jetzt eine Funktion/Methode, ein beliebiges Objekt oder Klasse ist, Hauptsache bar ist "callable".

Und ob man jetzt

Code: Alles auswählen

class Foo:
    class bar: pass
foo = Foo()
print foo.bar()
noch als astreinen Methodenaufruf bezeichnen kann...
Marky
User
Beiträge: 24
Registriert: Donnerstag 22. Januar 2009, 08:12

... ist fraglich.
Verstehe.
Gruß
Marky
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Zudem man noch Funktionen an Klassen binden kann, die vorher nicht Teil der Klasse waren.

Code: Alles auswählen

class A(object):
    pass

def foo(self):
    print "Methode der Instanz", self

A.foo = foo
a = A()
a.foo()
foo(a)
Da jetzt speziell zwischen Methode und Funktion zu unterscheiden scheint mir etwas künstlich. Natürlich, durch das binden an eine Klasse passiert etwas Magie, aber das ist mir zu verschwommen um da exakt unterscheiden zu wollen.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Leonidas hat geschrieben:Da jetzt speziell zwischen Methode und Funktion zu unterscheiden scheint mir etwas künstlich.
Warum machst du dann beim Importieren diesen Unterschied, d.h. behauptest, dass Funktionen importiert werden können, Methoden aber nicht? :?
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Man kann nur Dinge direkt aus dem Modul Namespace importieren. Da Methoden nunmal im Namespace einer Klasse definiert sind, kann man sie nicht importieren. Das trifft auch auf jedes andere Objekt zu, das nicht direkt im Modul Namespace liegt.

Du musst einfach Python's Klassenkonzept verstehen:

Die Methoden einer Python Klasse sind eigentlich Funktionen. Du gibst das "self" bei einer Methode nicht umsonst ein! Ein Aufruf der Form:

Code: Alles auswählen

a.b(args)
einer Instanz a wird konvertiert in das hier:

Code: Alles auswählen

a.__class__.b(a, args)
Deswegen gibt man das self an. Es gibt keinen Unterschied zwischen einer Methode und einer Funktion, außer dem, das eine an eine solche Methode immer die eigene Instanz als erstes Argument überliefert bekommt. Du könntest die Methode auch direkt über a.__class__.b aufrufen, und ihr irgendetwas anderes mitgeben, das als self behandelt werden soll.

Weiters, eine Funktion ist "nur" ein callable. Callable ist jedes Objekt, das eine __call__ Methode besitzt. Mach doch mal dir() auf eine Funktion. Du kannst jedes Spezialverhalten in Python durch __doubleunderscore__ Methoden emulieren, es gibt keine "bevorzugten" Objekte. Selbst Klassen sind Objekte, und bei der Instanziierung einer Klasse wird deren eigene __call__ Methode aufgerufen. (Deswegen hat bei solchen Metaklassen genannten Klassen das überschreiben von __call__ lustige Effekte).
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

bords0 hat geschrieben:Warum machst du dann beim Importieren diesen Unterschied, d.h. behauptest, dass Funktionen importiert werden können, Methoden aber nicht?
Mal abgesehen davon, ob dies geht oder nicht (es geht ja) und auch in eingedenk dessen, dass die Übergabe eines Callables als Parameter - für callbacks eben - durchaus Praxis ist: haltet ihr den Import von Methoden wirklich für sinnvoll?

Wenn ich eine Methode importiere, ist es dann eine Klassenmethode? Oder ist die Methode auf eine Instanz bezogen? Falls ja, auf welche? Ist das wärend der Laufzeit eines Programmes alles so klar und wirklich gewollt?

Methodenimport halte ich für einen Designfehler.

[edit]: str1442 hat vorhin darauf hingewiesen, Methodenimport nicht geht. Gut so.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

bords0 hat geschrieben:Warum machst du dann beim Importieren diesen Unterschied, d.h. behauptest, dass Funktionen importiert werden können, Methoden aber nicht? :?
Weil du an Klassen gebundene Funktionen (also dass was viele unter Methoden verstehen) eben nicht "aus der Klasse heraus" importieren kann. Macht auch keinen Sinn.

Wenn du die Methode an den Modulnamespace bindest, dann kannst du sie importieren. Das heißt für mich dann aber Funktion und ist ohne weiteres importierbar.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Klasse: Ich kopiere ein Beispiel aus IDLE heraus, in dem (sowohl gebundene als auch ungebundene) Methoden importiert werden, und drei Leute antworten und behaupten, man könne Methoden nicht importieren. Oder dass man die Methode dann nur Funktion nennen dürfte, aber auf keinen Fall Methode. Dass Python den Typ method ausgibt, wird ignoriert. Besonders bei bound methods ist es lustig, das als Funktion anzusehen...

Dass die Methode einen Namen auf Modulebene haben muss, ist ja klar. War ja in meinem Beispiel auch so. Und auch im Beispiel des OP wurde ja etwas auf Modulebene importiert. Und warum das keine Methode können sein soll, werde ich wohl nie verstehen (da es ja sein kann, wie jeder durch Ausprobieren oder Nachvollziehen des Beispieles selbst herausfinden kann).
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Du kannst keine Methode direkt aus einer Klasse importieren. Wenn du sie vorher mit einem Namen auf Modulebene bindest geht es natürlich. Dann bekommst du eine Unbound Method, der du immer explizit eine Instanz der Klasse übergeben musst. Das ist nichts weiter als eine Funktion. Und um das mal zu verdeutlichen:

Code: Alles auswählen

In [8]: def f(self):
   ...:     pass
   ...: 

In [10]: type(f)
Out[10]: <type 'function'>

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

In [12]: A.f = f

In [13]: type(A.f)
Out[13]: <type 'instancemethod'>

In [14]: A.f(False)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/str1442/<ipython console> in <module>()

TypeError: unbound method f() must be called with A instance as first argument (got bool instance instead)

In [15]: A().f()

In [16]: type(A().f)
Out[16]: <type 'instancemethod'>

In [17]: A().f
Out[17]: <bound method A.f of <__main__.A object at 0xb7cedb0c>>

In [18]: class C(object):
   ....:     @staticmethod
   ....:     def method():
   ....:         pass
   ....:     
   ....:     

In [19]: C.method
Out[19]: <function method at 0xb7676064>

In [20]: type(C.method) is type(f)
Out[20]: True
Beachte, das ein Dekorator die fertige Funktion zum Dekorieren benutzt, die zu diesem Zeitpunkt eigentlich eine Methode sein müsste: Warum ist die danach wieder vom Typ Funktion?

Gebundene Methoden sind Funktion mit bereits feststehendem Argument für self. Quasi functools.partial(class.method, instance).

Wenn du eine Methode ungebunden umherwirfst, bist du dafür verantwortlich, das sie ein Argument ihrer Instanz erhält beim Aufruf.

Achja, und ganz wichtig:

Code: Alles auswählen

In [23]: A.f
Out[23]: <unbound method A.f>

In [24]: A.f.im_func
Out[24]: <function f at 0xb766ee64>

In [25]: A.f.im_func is f
Out[25]: True
Methoden Objekte sind nur Wrapper.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

str1442 hat geschrieben: Ein Aufruf der Form:

Code: Alles auswählen

a.b(args)
einer Instanz a wird konvertiert in das hier:

Code: Alles auswählen

a.__class__.b(a, args)
Nicht immer, m.W. nur, wenn a.__class__.b eine ungebundene Methode ist (Python 2.x, in Python 3.0 evtl. anders). Jedenfalls geht es nicht mit gebundenen Methoden, built-in functions und Klassen. Vielleicht sind doch nicht alle callables so ähnlich, wie mancher meint.
str1442 hat geschrieben:Selbst Klassen sind Objekte, und bei der Instanziierung einer Klasse wird deren eigene __call__ Methode aufgerufen. (Deswegen hat bei solchen Metaklassen genannten Klassen das überschreiben von __call__ lustige Effekte).
Hm, eine Metaklasse ist doch üblicherweise eine Klasse, die von type erbt? (Genauer: Die Instanzen der Metaklasse sind Klassen - wie bei type eben.)
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

str1442 hat geschrieben:Du kannst keine Methode direkt aus einer Klasse importieren.
Natürlich nicht. Hat nie jemand behauptet.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

bords0 hat geschrieben:Nicht immer, m.W. nur, wenn a.__class__.b eine ungebundene Methode ist (Python 2.x, in Python 3.0 evtl. anders). Jedenfalls geht es nicht mit gebundenen Methoden, built-in functions und Klassen. Vielleicht sind doch nicht alle callables so ähnlich, wie mancher meint.
``a.b()`` ist immer ``getattr(a, "b")()`` Punkt. Natürlich sind nicht alle Callables gleich. Irgendwo muss ja Schluss sein, sonst würde man von __call__ zu __call__ springen.

bords0 hat geschrieben:Dass Python den Typ method ausgibt, wird ignoriert. Besonders bei bound methods ist es lustig, das als Funktion anzusehen...
Kann da nichts lustiges dran finden. Nur weil du einen Baum Eiche nennst ist es doch noch lange kein Baum mehr. Ja man nennt es halt Methode, ist aber nicht mehr als eine olle Funktion (genauer gesagt sind alle Python-Funktionen Closures).

Funktionen und Methoden sind in Python dasselbe, wie man sie nennt hängt vom Kontext ab.

Code: Alles auswählen

class Bar:
    foo = "Hello world"

// function is eine Funktion
def function(self):
    print self.foo

// method ist eine Methode
Bar.method = function

// function ist wieder eine Funktion
function = Bar.method
In Python 3 ist Bar.method auch keine "unbound method" mehr, sondern einfach eine Funktion.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Darii hat geschrieben:
bords0 hat geschrieben:Nicht immer, m.W. nur, wenn a.__class__.b eine ungebundene Methode ist (Python 2.x, in Python 3.0 evtl. anders). Jedenfalls geht es nicht mit gebundenen Methoden, built-in functions und Klassen. Vielleicht sind doch nicht alle callables so ähnlich, wie mancher meint.
``a.b()`` ist immer ``getattr(a, "b")()`` Punkt.
Schon klar. Im Zitat geht es aber ja gerade um etwas anderes, nämlich a.__class__.b.
Darii hat geschrieben:
bords0 hat geschrieben:Dass Python den Typ method ausgibt, wird ignoriert. Besonders bei bound methods ist es lustig, das als Funktion anzusehen...
Kann da nichts lustiges dran finden. Nur weil du einen Baum Eiche nennst ist es doch noch lange kein Baum mehr. Ja man nennt es halt Methode, ist aber nicht mehr als eine olle Funktion (genauer gesagt sind alle Python-Funktionen Closures).
Ob der Typ method oder function ist, ist schon ein Unterschied. Probier mal dir(). Eine Methode ist halt ein (dünner) Wrapper um eine Funktion.
Darii hat geschrieben: Funktionen und Methoden sind in Python dasselbe, wie man sie nennt hängt vom Kontext ab.

Code: Alles auswählen

class Bar:
    foo = "Hello world" 

// function is eine Funktion
def function(self):
    print self.foo

// method ist eine Methode
Bar.method = function

// function ist wieder eine Funktion
function = Bar.method
In Python 3 ist Bar.method auch keine "unbound method" mehr, sondern einfach eine Funktion.
Der Typ von function ist am Schluss "unbound method". Dass der Wrapper um die Funktion so dünn ist, dass man ihn auch weglassen kann, hat man für Python 3.0 erkannt und umgesetzt, richtig. Deshalb habe ich auch explizit von bound methods geredet. Die brauchen den Wrapper um die Funktion, die als Attribut im_func zur Verfügung steht (wie wir wohl alle wissen). Aber der Wrapper ist da.

Und nochmal: Wenn Methoden und Funktionen dasselbe sind, warum sollte man dann Funktionen importieren können, aber keine Methoden?
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Methoden sind ein Konzept und kein Typ und die Übergänge zwischen Funktionen und Methoden sind fließend. Natürlich kann man Objekte vom Typ method aus Modulen importieren. Aber es ist kein Verbrechen die dann Funktion zu nennen.
James Whitcomb Riley hat geschrieben:When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Nicht immer, m.W. nur, wenn a.__class__.b eine ungebundene Methode ist (Python 2.x, in Python 3.0 evtl. anders). Jedenfalls geht es nicht mit gebundenen Methoden, built-in functions und Klassen. Vielleicht sind doch nicht alle callables so ähnlich, wie mancher meint.
Hä? Ich hab doch auch von ungebundenen Methoden geredet. Wenn du die gebundene Methode "b" einer Instanz a aufrufst, wird das umgewandelt in <Klasse von a>.<ungebundene Methode b>(a). Und <Klasse von a> konnte ich oben besser durch "a.__class__" darstellen. Also a.__class__.b(a). Das trifft natürlich nicht auf alle Callables zu.
Hm, eine Metaklasse ist doch üblicherweise eine Klasse, die von type erbt? (Genauer: Die Instanzen der Metaklasse sind Klassen - wie bei type eben.)
Genau. Die Standard Metaklasse type besitzt eine ungebundene __call__ Methode. Eine Instanz (also eine "normale" Klasse) von type hat also eine "gebundene __call__ Methode". Sobald man eine solche Klasse instanziieren will, wird bei einem Aufruf der Form

Code: Alles auswählen

instance = Class()
__call__ aufgerufen. __call__ sorgt dafür, das der Konstruktor __new__ der Klasse und deren __init__ Methode aufgerufen wird. So könnte man mit einer Metaklasse den Aufruf von __init__ für jede neue Instanz einer Klasse (der Metaklasse) abstellen.
Und nochmal: Wenn Methoden und Funktionen dasselbe sind, warum sollte man dann Funktionen importieren können, aber keine Methoden?
Klar sollte man es tun können. Macht aber wenig Sinn. Wenn du eine ungebundene importierst musst du sowieso für die Instanz sorgen und ansonsten kannst du nur von einer in einem Modul global gespeicherten Instanz einer Klasse auf eine gebundene Methode zugreifen. Und wozu sollte man deren Methoden irgendwo rumreichen? Zumal sowas imho sowieso komisch riecht.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Achja:
Irgendwo muss ja Schluss sein, sonst würde man von __call__ zu __call__ springen.

Code: Alles auswählen

>>> class A(object):
...  def test(self): pass
... 
>>> a = A()
>>> a.test.__call__
<method-wrapper '__call__' of instancemethod object at 0xb7d7202c>
>>> a.test.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0xb7d89d4c>
>>> a.test.__call__.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0xb7d89e4c>
>>> a.test.__call__.__call__.__call__.__call__()
>>> 
:D
Antworten