2014-09-26 3 views
9

Я пытаюсь перегрузить некоторые методы встроенной строки. Я знаю, что нет действительно законное использование случая для этого, но поведение по-прежнему ошибки мне, так что я хотел бы получить объяснение того, что происходит здесь:Python перегрузочные примитивы

Использование python2, и forbiddenfruit модуль.

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

Как вы можете видеть, функция __repr__, как были успешно перегружены, но на самом деле не вызывается, если мы просим о представлении. Почему это?

Тогда, как бы вы сделали, чтобы получить ожидаемое поведение:

>>> 'foo' 
'bar' 

Там нет ограничений по настройке пользовательской среды, если восстановление питона является то, что он принимает, так и быть, но я на самом деле дон Не знаю, с чего начать, и я все еще надеюсь, что есть более простой способ:

+2

Какую проблему вы пытаетесь решить (что заставляет вас перегружать встроенные методы)? – goncalopp

+0

Возможный дубликат [Переопределение специальных методов в экземпляре] (http://stackoverflow.com/questions/10376604/overriding-special-methods-on-an-instance) – njzk2

+0

@goncalopp: То, что я пытаюсь сделать, это имеют запущенную оболочку python, в которой вызов __repr__, вызываемый для любой строки, заменяется моим собственным методом. Его не нужно использовать в qny-программе, которая должна запускаться на любом другом интерпретаторе python, так как я сказал, что обезьяна-паттинг моего собственного питона будет в порядке, если бы я знал, как это сделать. – Centime

ответ

6

Первое, что нужно отметить, - это то, что делает 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 экземпляра самого

так тиражирования потребовалось бы взлом самой 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).

+0

Это! Спасибо, Veedrac! Это прекрасно, с большим количеством объяснений и всего. Я только на полпути, но это больше, чем я надеялся. – Centime

+0

Он [не работает] (https://gist.github.com/metaperl/99103cdfbe675afeaa38564ebcea7288) –

+0

@TerrenceBrannon Действительно, и я объясню, почему этого не происходит в этом ответе. Вероятно, я не был достаточно ясен, поэтому, если бы вы объяснили, почему вы думаете, что я сказал иначе, это действительно помогло бы мне устранить любые двусмысленности. – Veedrac

Смежные вопросы