Первое, что нужно отметить, - это то, что делает forbiddenfruit
, это не влияет на repr
. Это не частный случай для str
, он просто не работает так:
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'
Я недавно нашел a much simpler way of doing what forbiddenfruit
does благодаря сообщению HYRY:
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'"
Итак, мы знаем, несколько anticlimactically , что что-то еще происходит.
Вот the source for builtin_repr
:
builtin_repr(PyModuleDef *module, PyObject *obj)
/*[clinic end generated code: output=988980120f39e2fa input=a2bca0f38a5a924d]*/
{
return PyObject_Repr(obj);
}
И для PyObject_Repr
(секций): Опущенные
PyObject *
PyObject_Repr(PyObject *v)
{
PyObject *res;
res = (*v->ob_type->tp_repr)(v);
if (res == NULL)
return NULL;
}
Важным моментом является то, что вместо того, смотрю g вверх в dict
, он просматривает атрибут «cached» tp_repr
.
Here's what happens при установке атрибута с чем-то вроде 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);
}
Первая часть вещь мешает вам изменения встроенных типов. Затем он устанавливает атрибут в общем случае (PyObject_GenericSetAttr
) и, что важно, обновляет слоты.
Если вас интересует, как это работает, it's available here.Решающие моменты:
так тиражирования потребовалось бы взлом самой PyTypeObject
типа.
Если вы хотите сделать это, возможно, самая простая попытка попробовать (временно?) Установить type->tp_flags & Py_TPFLAGS_HEAPTYPE
на класс str
. Это позволит установить атрибут как обычно. Конечно, нет никаких гарантий, что это не приведет к краху вашего переводчика.
Это не то, что я хочу сделать (особенно не через ctypes
), если мне действительно не нужно, поэтому я предлагаю вам ярлык.
Вы пишете:
Тогда, как бы вы сделали, чтобы получить ожидаемое поведение:
>>> 'foo'
'bar'
Это на самом деле довольно легко с помощью sys.displayhook
:
sys.displayhook
является вызванный результатом оценки expression, введенного в взаимодействии ive сеанс Python. Отображение этих значений можно настроить, назначив другую функцию с одним аргументом sys.displayhook
.
И вот пример:
import sys
old_displayhook = sys.displayhook
def displayhook(object):
if type(object) is str:
old_displayhook('bar')
else:
old_displayhook(object)
sys.displayhook = displayhook
А потом ...
'foo'
#>>> 'bar'
123
#>>> 123
С философской точки почемуrepr
будет кэшируются так (!) , сначала рассмотрим:
1 + 1
Было бы больно, если бы это было необходимо найти в словаре перед вызовом, CPython медленнее, так как CPython решил кэшировать поиск стандартными методами dunder (double underscore). __repr__
является одним из тех, даже если он менее распространен, чтобы оптимизировать поиск. Это по-прежнему полезно для быстрого форматирования ('%s'%s
).
Какую проблему вы пытаетесь решить (что заставляет вас перегружать встроенные методы)? – goncalopp
Возможный дубликат [Переопределение специальных методов в экземпляре] (http://stackoverflow.com/questions/10376604/overriding-special-methods-on-an-instance) – njzk2
@goncalopp: То, что я пытаюсь сделать, это имеют запущенную оболочку python, в которой вызов __repr__, вызываемый для любой строки, заменяется моим собственным методом. Его не нужно использовать в qny-программе, которая должна запускаться на любом другом интерпретаторе python, так как я сказал, что обезьяна-паттинг моего собственного питона будет в порядке, если бы я знал, как это сделать. – Centime