我正在尝试重载字符串内置的一些方法。 我知道没有真正合法的用例,但这种行为仍然让我感到烦恼,所以我想得到一个解释这里发生的事情:
使用Python2和forbiddenfruit
模块。
>>> from forbiddenfruit import curse
>>> curse(str, '__repr__', lambda self:'bar')
>>> 'foo'
'foo'
>>> 'foo'.__repr__()
'bar'
正如您所看到的,__repr__
函数已成功重载,但在我们要求表示时实际上并未调用。那是为什么?
然后,您将如何做到预期的行为:
>>> 'foo'
'bar'
没有关于设置自定义环境的限制,如果重建python是它所需要的,那就这样吧,但我真的不知道从哪里开始,我仍然希望有一种更简单的方法:)
答案 0 :(得分: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'"
所以,我们知道,有点讽刺的是,其他事情正在发生。
此处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;
}
重要的一点是,它不是在dict
中查找,而是查找&#34;缓存&#34; tp_repr
属性。
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'
对评估在交互式Python会话中输入的expression的结果调用
sys.displayhook
。可以通过为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
如果在调用之前必须在字典中查找__add__
,那将是一件痛苦的事情,CPython很慢,所以CPython决定将查找缓存到标准dunder(双下划线)方法。 __repr__
就是其中之一,即使需要优化查找的情况也不常见。这对于快速格式化('%s'%s
)仍然很有用。