Cen*_*ime 9 python overloading
我正在尝试重载字符串内置的一些方法.我知道没有真正合法的用例,但这种行为仍然让我感到烦恼,所以我想得到一个解释这里发生的事情:
使用Python2和forbiddenfruit模块.
>>> from forbiddenfruit import curse
>>> curse(str, '__repr__', lambda self:'bar')
>>> 'foo'
'foo'
>>> 'foo'.__repr__()
'bar'
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,该__repr__函数已成功重载,但在我们要求表示时实际上并未调用.这是为什么?
那么,你将如何做到预期的行为:
>>> 'foo'
'bar'
Run Code Online (Sandbox Code Playgroud)
没有关于设置自定义环境的限制,如果重建python是它所需要的,那就这样吧,但我真的不知道从哪里开始,我仍然希望有一个更简单的方法:)
首先要注意的是,无论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'
Run Code Online (Sandbox Code Playgroud)
我最近发现做的更简单的方法forbiddenfruit确实得益于后由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'"
Run Code Online (Sandbox Code Playgroud)
因此,我们知道,有点讽刺的是,其他事情正在发生.
builtin_repr(PyModuleDef *module, PyObject *obj)
/*[clinic end generated code: output=988980120f39e2fa input=a2bca0f38a5a924d]*/
{
return PyObject_Repr(obj);
}
Run Code Online (Sandbox Code Playgroud)
并为PyObject_Repr(部分省略):
PyObject *
PyObject_Repr(PyObject *v)
{
PyObject *res;
Run Code Online (Sandbox Code Playgroud)
res = (*v->ob_type->tp_repr)(v);
if (res == NULL)
return NULL;
Run Code Online (Sandbox Code Playgroud)
}
Run Code Online (Sandbox Code Playgroud)
重要的是,它不是查找a dict,而是查找"缓存" 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);
}
Run Code Online (Sandbox Code Playgroud)
第一部分是阻止您修改内置类型的事情.然后它通常设置属性(PyObject_GenericSetAttr),并且至关重要地更新插槽.
它不是导出的函数
它修改了PyTypeObject实例本身
所以复制它需要黑客攻击PyTypeObject类型本身.
如果你想这样做,也许尝试最简单的事情将(暂时α)设置type->tp_flags & Py_TPFLAGS_HEAPTYPE的str类.这将允许正常设置属性.当然,不能保证这不会让翻译失败.
ctypes除非我真的需要,否则这不是我想做的事情(特别是不是),所以我为你提供了一条捷径.
你写:
那么,你将如何做到预期的行为:
Run Code Online (Sandbox Code Playgroud)>>> 'foo' 'bar'
这实际上很容易使用sys.displayhook:
sys.displayhook调用在交互式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
Run Code Online (Sandbox Code Playgroud)
然后... (!)
'foo'
#>>> 'bar'
123
#>>> 123
Run Code Online (Sandbox Code Playgroud)
关于为什么 repr会被缓存的哲学观点,首先要考虑:
1 + 1
Run Code Online (Sandbox Code Playgroud)
如果__add__在调用之前必须在字典中查找,CPython的速度很慢,那将是一种痛苦,因此CPython决定将查找缓存到标准的dunder(双下划线)方法.__repr__即使需要优化查找不太常见,也是其中之一.这对于'%s'%s快速保持格式化()仍然很有用.
| 归档时间: |
|
| 查看次数: |
473 次 |
| 最近记录: |