Seite 3 von 3

Verfasst: Dienstag 2. Juni 2009, 15:51
von str1442
Achso - Bitte um Entschulding, da hatte ich "methodDictionaries" falsch aufgefasst. Nun, die einfachste Möglichkeit dafür bietet __setattr__ und __delattr__, ich nutze eine Closure:

Code: Alles auswählen

In [5]: def include(into=()):
   ...:     class MetaCls(type):
   ...:         def __setattr__(self, name, value):
   ...:             if isinstance(value, types.FunctionType):
   ...:                 for cls in into:
   ...:                     setattr(cls, name, value)
   ...:             super(MetaCls, self).__setattr__(name, value)
   ...:         def __delattr__(self, name):
   ...:             if isinstance(getattr(self, name), types.MethodType):
   ...:                 for cls in into:
   ...:                     delattr(cls, name)
   ...:             super(MetaCls, self).__delattr__(name)
   ...:     return MetaCls
   ...: 

In [7]: import types

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

In [9]: class B(object):
   ...:     pass
   ...: 

In [10]: class C(object):
   ....:     __metaclass__ = include((A, B))
   ....:     pass
   ....: 

In [11]: C.a = lambda self: 42

In [12]: print A().a()
42

In [13]: print B().a()
42

In [14]: print C().a()
42

In [15]: del C.a

In [16]: print A().a()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)

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

AttributeError: 'A' object has no attribute 'a'

In [17]: print B().a()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)

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

AttributeError: 'B' object has no attribute 'a'

In [18]: print C().a()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)

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

AttributeError: 'C' object has no attribute 'a'

In [19]: def greet(self):
   ....:     print "Greet from: ", self
   ....:     
   ....:     

In [20]: C.greet = greet

In [21]: A().greet()
Greet from:  <__main__.A object at 0x929ff8c>

In [22]: B().greet()
Greet from:  <__main__.B object at 0x929ff8c>

In [23]: C().greet()
Greet from:  <__main__.C object at 0x95f1c0c>
Wahlweise dann natürlich mit Initialisierung von bereits vorhandenen Methoden. Ob man __dict__ in Metaklassen genauso manipulieren kann, wie __dict__ einer Instanz, weiß ich nicht - naive Versuche scheitern. Aber da man sich Metaklassen selbst zusammenbauen kann wie man will, bin ich zuversichtlich, das es da sicherlich einen Weg gibt (Eventuell über die Metaklasse der Metaklasse ;)). Manipulationen an __dict__ funktionieren mit __get/set/delattr__ aber doch recht gut (die Möglichkeit hätte ich sowieso vorher in Betracht ziehen sollen).

Verfasst: Dienstag 2. Juni 2009, 19:05
von sma
Eine Klasse in Smalltalk hat ein dictionary - das method dictionary - wo eben die Methoden drin stecken. Das entspricht in etwa dem `__dict__` einer Klasse in Python. Dort stecken allerdings eigentlich beliebige Objekte drin und Funktionen, die im Kontext eines Exemplars von dort ausgelesen werden, verwandeln sich auf "magische" Weise in gebundene Methoden.

Das Beispiel verstehe ich allerdings immer noch nicht. Du kopierst ja immer noch. Wie häufig noch muss ich sagen, dass es gerade darum geht, dass man nicht kopieren will/soll/braucht.

Normalfall in Smalltalk:

Code: Alles auswählen

    +-------+
    | Class |
    |   *--------> +------+
    +-------+      | Dict |
        ^          |  *-------> +--------+
        |          +------+     | Method |
        |                       +--------+
    +-------+
    | Class |
    |   *-------> ...
    +-------+
Jede Klasse (also jedes Exemplar einer Metaklasse) hat eine Exemplarvariable `methodDict`, in dem ein Exemplar von `MethodDictionary` (Unterklasse von `Dictionary`) steckt, das `CompiledMethod`-Exemplare unter Namen (Exemplare von `Symbol`) abgelegt hat. Die Klasse hat eine weitere Exemplarvariable `superclass`, die die Oberklasse enthält.

Da die method dictionaries eigenständige Objekte sind, kann ich jetzt dies machen:

Code: Alles auswählen

                +------+
                |ClassC|
                +------+
       ^            |          ^
       |            v          |
    +------+   +-------+   +------+
    |Vrtual|-->| MDict |<--|Vrtual|
    +------+   +-------+   +------+
       ^                       ^
       |                       |
    +------+               +------+  
    |ClassA|               |ClassB|  
    +------+               +------+
Und so funktionieren Mixin-Module bei Ruby.

Stefan

Verfasst: Mittwoch 3. Juni 2009, 01:13
von str1442
Mein Beispiel tut genau das, was du erreichen wolltest - Ich registriere mit include() mehrere Klassen und diese bekommen sofort sämtliche Methoden der "Server Klasse" übertragen bei Änderung.

Ja, es ist kein einzelnes, für alle Klassen geltendes Dict. Das braucht man zur Lösung des Problems aber auch nicht. Da der Namensraum eines beliebigen Objektes in Python aber sowieso in __dict__ gespeichert wird und somit Zuweisungen zu Objekten mittels dem "=" Operator nur "syntactic Sugar" dafür sind, überwacht meine MetaCls ob bestimmte Funktionen gesetzt werden oder Methoden gelöscht werden und passt die anderen Objekte entsprechend an. Da aber, wie du ja auch schon sagst, __dict__ von type() Objekten speziell zu sein scheint, kann man es so direkt (auf type() Exemplaren und nur dort) nicht anpassen. Ich vermute aber stark, daß man dieses Verhalten irgendwie mittels einer Subklasse von type anpassen kann.

Da du ja explizt erwähnst, daß in diesem dict in Smalltalk nur Methoden gespeichert werden, ist es so direkt mit __dict__ sowieso nicht vergleichbar. Will man also unbedingt eine Lösung mit einem Methoden-Wörterbuch haben, könnte man das in einer eigenen Metaklasse selbst definieren (ala methods_dict Attribute) und __setattr__ und __delattr__ nutzen, um eben dann eben dieses Dict zu beeinflussen. Dann könnte man einfach alle Dicts unter den Klassen aufteilen.

Ich sehe da aber nicht besonders viel Sinn darin. Es geht doch gar nicht darum, ob man nun genau wie in Smalltalk solche Method Dictionaries einfach implementieren kann; Ähnliches Verhalten wird doch mit __set und delattr__ auch erreicht. Insofern ist das kein Argument für eine höhere Ausdrucksstärke Smalltalks, im Endeffekt kann man sich mittels Metaklassen und __setattr__ usw oder komplett eigenen Definitionen und Attributen alles zurechtbasteln.

Zu meinen Beispiel: Kopiert wird da nichts. Die Funktionen bleiben überall gleich. Es wird nur explizit überprüft, ob alle zu überwachenden Klassen besitimmte Methoden kennen und diese gegebenenfalls gelöscht bzw Funktionen überall eingetragen. Mein Beispiel sollte nur aufzeigen, wie man das von dir geforderte Verhalten einfach nachbilden kann.

Auf die Gefahr hin mich zu wiederzuholen (ist schon spät): Würde ich nun diesen Mechanismus genauso nachbauen wollen, würde ich eine Subklasse von type erstellen, welche ein method_dict Dictionary bekommt und mittels __setattr__ und __delattr__ alle gefundenen Funktionen in dieses Dict einträgt. Dann könnte man einfach die Dictionaries zu einem verbinden. Im Endeffekt käme aber nichts anderes bei heraus, wie das, was ich in meinen letzten Posting aufzeigte. Vielleicht morgen.