2014-09-26 7 views
9

Ich versuche, einige Methoden der integrierten Zeichenfolge zu überladen. Ich weiß, es Anwendungsfall dafür, aber das Verhalten immer noch nervt mich so würde Ich mag bekommen eine Erklärung dessen, was geschieht hier nicht wirklich legitim ist:Python-Overload-Primitive

Mit Python2 und das forbiddenfruit Modul.

>>> from forbiddenfruit import curse 
>>> curse(str, '__repr__', lambda self:'bar') 
>>> 'foo' 
'foo' 
>>> 'foo'.__repr__() 
'bar' 

Wie Sie sehen können, die __repr__ Funktion als erfolgreich überlastet, ist aber nicht tatsächlich aufgerufen wird, wenn, wenn wir für eine Darstellung fragen. Warum das?

Dann, wie würden Sie tun das erwartete Verhalten zu erhalten:

>>> 'foo' 
'bar' 

Es gibt keine Einschränkung über eine benutzerdefinierte Umgebung einrichten, wenn Python Wiederaufbau ist, was es braucht, soll es so sein, aber ich wirklich don Ich weiß nicht, wo ich anfangen soll, und ich hoffe immer noch, dass es einen einfacheren Weg gibt :)

+2

Welches Problem versuchen Sie zu lösen (wodurch Sie die integrierten Methoden überladen möchten)? – goncalopp

+0

mögliches Duplikat von [Überschreiben spezieller Methoden auf einer Instanz] (http://stackoverflow.com/questions/10376604/overriding-special-methods-on-an-instance) – njzk2

+0

@goncalopp: Was ich versuche zu tun ist zu Habe eine laufende Python-Shell, in der der für eine beliebige Zeichenfolge aufgerufene __repr__ durch eine eigene Methode ersetzt wurde. Es muss nicht im qny-Programm verwendet werden, das auf einem anderen Python-Interpreter ausgeführt werden muss. Wie gesagt, Monkey-Patchen meiner eigenen Python wäre in Ordnung, wenn ich nur die Art und Weise kenne, wie ich das machen könnte. – Centime

Antwort

6

Die erste Sache zu beachten ist, dass was auch immer forbiddenfruit tut, hat es keinen Einfluss repr überhaupt. Dies ist kein Sonderfall für str, es funktioniert einfach nicht so:

import gc 

underlying_dict = gc.get_referents(str.__dict__)[0] 
underlying_dict["__repr__"] = lambda self: print("I am a str!") 

"hello".__repr__() 
#>>> I am a str! 

repr("hello") 
#>>> "'hello'" 

So wissen wir, etwas anticlimactically:

import forbiddenfruit 

class X: 
    repr = None 

repr(X()) 
#>>> '<X object at 0x7f907acf4c18>' 

forbiddenfruit.curse(X, "__repr__", lambda self: "I am X") 

repr(X()) 
#>>> '<X object at 0x7f907acf4c50>' 

X().__repr__() 
#>>> 'I am X' 

X.__repr__ = X.__repr__ 

repr(X()) 
#>>> 'I am X' 

ich a much simpler way of doing what forbiddenfruit does dank einem Beitrag von HYRY kürzlich gefunden , dass etwas anderes vor sich geht.

Hier the source for builtin_repr:

builtin_repr(PyModuleDef *module, PyObject *obj) 
/*[clinic end generated code: output=988980120f39e2fa input=a2bca0f38a5a924d]*/ 
{ 
    return PyObject_Repr(obj); 
} 

Und für PyObject_Repr (Abschnitte elided):

PyObject * 
PyObject_Repr(PyObject *v) 
{ 
    PyObject *res; 
res = (*v->ob_type->tp_repr)(v); 
    if (res == NULL) 
     return NULL; 
} 

Der wichtige Punkt ist, dass anstelle von eng g in einem dict, es sucht das "im Cache gespeicherte" tp_repr Attribut.

Here's what happens, wenn Sie setzen Sie das Attribut mit so etwas wie TYPE.__repr__ = new_repr:

static int 
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) 
{ 
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { 
     PyErr_Format(
      PyExc_TypeError, 
      "can't set attributes of built-in/extension type '%s'", 
      type->tp_name); 
     return -1; 
    } 
    if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0) 
     return -1; 
    return update_slot(type, name); 
} 

Der erste Teil ist das, was Sie von modifizierenden eingebauten Typen zu verhindern. Dann setzt es das Attribut generisch (PyObject_GenericSetAttr) und aktualisiert die Slots entscheidend.

Wenn Sie interessiert sind, wie das funktioniert, it's available here.Die entscheidenden Punkte sind:

  • Es ist nicht eine exportierte Funktion und

  • Es ändert die PyTypeObject Instanz selbst

so replizieren würde es sich Hacker in die PyTypeObject Art erfordern.

Wenn Sie dies tun möchten, wäre wahrscheinlich die einfachste Sache zu versuchen wäre (vorübergehend?) type->tp_flags & Py_TPFLAGS_HEAPTYPE auf der str Klasse. Damit könnte das Attribut normal gesetzt werden. Natürlich gibt es keine Garantie, dass dies Ihren Interpreter nicht zum Absturz bringt.

Dies ist nicht, was ich tun möchte (vor allem nicht durch ctypes), es sei denn, ich muss wirklich, so biete ich Ihnen eine Verknüpfung.

Sie schreiben:

Dann, wie würden Sie das erwartete Verhalten zu erhalten tun:

>>> 'foo' 
'bar' 

Das ist eigentlich ganz einfach sys.displayhook mit:

sys.displayhook ist aufgerufen auf das Ergebnis der Auswertung einer expression in einer Interaktion eingegeben ive Python-Sitzung. Die Anzeige dieser Werte kann angepasst werden, indem sys.displayhook eine weitere Einargumentfunktion zugewiesen wird.

Und hier ist ein Beispiel:

import sys 

old_displayhook = sys.displayhook 
def displayhook(object): 
    if type(object) is str: 
     old_displayhook('bar') 
    else: 
     old_displayhook(object) 

sys.displayhook = displayhook 

Und dann ...

'foo' 
#>>> 'bar' 

123 
#>>> 123 

Auf dem philosophischen Punkt warumrepr würde als so zwischengespeichert werden (!) , zuerst betrachten:

1 + 1 

Es wäre ein Schmerz, wenn dies __add__ in einem Wörterbuch vor dem Aufruf nachschlagen müsste, CPython ist langsam wie es ist, so CPython entschieden, Lookups nach Standard-Dunder (Doppel-Unterstrich) Methoden zu cachen. __repr__ ist eine davon, auch wenn es weniger üblich ist, dass die Suche optimiert wird. Dies ist immer noch nützlich, um die Formatierung ('%s'%s) schnell zu halten.

+0

Dies! Danke Veedrac! Es ist perfekt, mit vielen Erklärungen und allem. Ich bin erst auf halbem Weg, aber es ist schon mehr als ich mir erhofft hatte. – Centime

+0

Es [funktioniert nicht] (https://gist.github.com/metaperl/99103cdfbe675afeaa38564ebcea7288) –

+0

@TerrenceBrannon Tatsächlich, und ich erkläre warum es nicht in dieser Antwort. Es ist wahrscheinlich, dass ich nicht klar genug gewesen bin, also wenn du mir erklärst, warum du denkst, ich hätte es anders gesagt, würde es mir wirklich helfen, irgendwelche Unklarheiten auszuräumen. – Veedrac