2012-04-01 3 views
2

Ich habe eine Klasse, die durch Rücksendung der Attribute seiner „Reihe“ Attribut, entlang der folgenden Zeilen Objektattribut Zugriff Überlastungen:Ist es möglich, einen Aufruf von Vars (Objekt) in Python zu überladen?

from collections import namedtuple 
class MyObj(object): 
    def __init__(self, y, z): 
     r = namedtuple('row', 'a b') 
     self.row = r(y, z) 
     self.arbitrary = True 
    def __getattr__(self, attr): 
     return getattr(self.row, attr) 
    def __dir__(self): 
     return list(self.row._fields) 

In [2]: m = MyObj(1, 2) 
In [3]: dir(m) 
Out[3]: ['a', 'b'] 
In [4]: m.a 
Out[4]: 1 
In [5]: vars(m) 
Out[5]: {'arbitrary': True, 'row': row(a=1, b=2)} 
In [6]: output = '{a} -> {b}' 
In [7]: output.format(**vars(m.row)) 
Out[7]: '1 -> 2' 
In [8]: output.format(**vars(m)) 
KeyError: 'a' 

Da ich ziemlich oft Zeichenfolge Formatierung mit vars() Ich mag würde zu auf die Zeilen Attribute direkt vom Aufruf von vars() zugreifen können. Ist das möglich?


bearbeiten folgende aaronsterling Antwort

Der Schlüssel dies zu lösen, dank Aarons Zeiger, für __dict__ in __getattribute__

from collections import namedtuple 
class MyObj(object): 
    def __init__(self, y, z): 
     r = namedtuple('row', 'a b') 
     self.row = r(y, z) 
     self.arbitrary = True 
    def __getattr__(self, attr): 
     return getattr(self.row, attr) 
    def __getattribute__(self, attribute): 
     if attribute == '__dict__': 
      return self.row._as_dict() 
     else: 
      return object.__getattribute__(self, attribute) 
    def __dir__(self): 
     return list(self.row._fields) 

In [75]: m = MyObj(3, 4) 

In [76]: m.a 
Out[76]: 3 

In [77]: vars(m) 
Out[77]: OrderedDict([('a', 3), ('b', 4)]) 
+0

Ist das für 2.7? – aaronasterling

+0

@Aaronasterling: Ja, ich hätte erwähnen sollen, dass dies für Python 2.7 – rorycl

Antwort

2

Die docs sind so freundlich zu überprüfen ist, angeben dass vars funktioniert, indem das Attribut des aufgerufenen Objekts zurückgegeben wird. Daher macht das Überschreiben __getattribute__ den Trick. Y

Ich Unterklasse dict (aber siehe später), um die __str__ Funktion zu überschreiben. Die Unterklasse akzeptiert eine Funktion str_func, die aufgerufen wird, um eine Zeichenfolgendarstellung zurückzugeben, wie Ihr __dict__ Objekt angezeigt werden soll. Dazu erstellt es ein reguläres Wörterbuch mit den gewünschten Einträgen und ruft dann str auf.

Dies ist sehr hacky. Insbesondere wird es einen Code brechen, die etwas auf dem Tun wie

myobj.__dict__[foo] = bar 

Dieser Code ein Phantom-Wörterbuch aktualisieren wird jetzt und nicht die wirkliche abhängt.

Eine viel robustere Lösung würde davon abhängen, alle Methoden vollständig zu ersetzen, die Werte auf dem SpoofedDict mit Methoden setzen, die tatsächlich myobj.__dict__ aktualisieren. Dies würde SpoofedDict Instanzen erfordern, um eine Referenz auf myobj.__dict__ zu halten. Dann müssten die Methoden, die Werte lesen, natürlich auch diese aus myobj.__dict__ holen.

An diesem Punkt verwenden Sie besser collections.Mapping, um eine benutzerdefinierte Klasse anstelle von Unterklassen von dict zu erstellen.

Hier ist der Proof of Concept-Code, hackish es auch sein mag:

from collections import namedtuple 


class SpoofDict(dict): 
    def __init__(self, *args, **kwargs): 
     self.str_func = kwargs['str_func'] 
     del kwargs['str_func'] 

     dict.__init__(self, *args, **kwargs) 

    def __str__(self): 
     return self.str_func() 


class MyObj(object): 
    def __init__(self, y, z): 
     r = namedtuple('row', 'a b') 
     self.row = r(y, z) 
     self.arbitrary = True 
    def __getattr__(self, attr): 
     return getattr(self.row, attr) 

    def __dir__(self): 
     return list(self.row._fields) 

    def str_func(self): 
     attrs = list(self.row._fields) 
     str_dict = {} 
     row = object.__getattribute__(self, 'row') 
     for attr in attrs: 
      str_dict[attr] = getattr(row, attr) 
     return str(str_dict) 

    def __getattribute__(self, attribute): 
     if attribute == '__dict__': 
      spoof_dict = SpoofDict(str_func=self.str_func) 
      spoof_dict.update(object.__getattribute__(self, '__dict__')) 
      return spoof_dict     
     else: 
      return object.__getattribute__(self, attribute) 


if __name__=='__main__': 
    m = MyObj(1, 2) 
    print "dir(m) = {0}".format(dir(m)) 
    print "vars(m) = {0}".format(vars(m)) 
    print "m.row = {0}".format(m.row) 
    print "m.arbitrary = {0}".format(m.arbitrary) 
+1

+1 für interessante Analyse ist. Übrigens scheinen die Dokumente für Python 3.2 nicht zu versprechen, dass "__dict__" verwendet wird, daher ist es möglicherweise nicht sehr sicher. – max

+0

@max Gut aussehend. Das ist wahrscheinlich eine schreckliche Idee. Der Fragesteller wäre besser dran, wenn er der Klasse nur eine Methode zur Verfügung stellt, um das gewünschte Diktat oder die Zeichenfolge zurückzugeben. – aaronasterling

Verwandte Themen