首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python重载原语

Python重载原语
EN

Stack Overflow用户
提问于 2014-09-26 13:57:10
回答 1查看 915关注 0票数 10

我试着让字符串内置的一些方法过载。我知道这没有真正合法的用例,但是这种行为仍然困扰着我,所以我想了解一下这里发生了什么:

使用Python2和forbiddenfruit模块。

代码语言:javascript
复制
>>> from forbiddenfruit import curse
>>> curse(str, '__repr__', lambda self:'bar')
>>> 'foo'
'foo'
>>> 'foo'.__repr__()
'bar'

正如您所看到的,__repr__函数已经成功重载,但是当我们请求表示时实际上并没有调用它。为什么会这样呢?

那么,如何才能得到预期的行为:

代码语言:javascript
复制
>>> 'foo'
'bar'

建立一个自定义环境没有任何限制,如果重建python是需要的,那么尽管如此,但我真的不知道从哪里开始,我仍然希望有一个更简单的方法:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-09-27 05:23:59

首先要注意的是,无论forbiddenfruit在做什么,它根本没有影响到repr。对于str来说,这不是一个特例,它不是这样工作的:

代码语言:javascript
复制
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'

我最近发现了有吗?,多亏了海瑞的一篇文章

代码语言:javascript
复制
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'"

所以我们知道,从某种程度上说,还有别的事情在发生。

这是builtin_repr

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

对于PyObject_Repr (被删除的部分):

代码语言:javascript
复制
PyObject *
PyObject_Repr(PyObject *v)
{
    PyObject *res;
代码语言:javascript
复制
    res = (*v->ob_type->tp_repr)(v);
    if (res == NULL)
        return NULL;
代码语言:javascript
复制
}

重要的一点是,它不是在dict中查找,而是查找“缓存的”tp_repr属性。

下面是发生的事情当您用类似于TYPE.__repr__ = new_repr的东西设置属性时

代码语言:javascript
复制
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),关键是更新时隙。

如果你对它的工作方式感兴趣,这里可以买到。要点是:

  • 它不是导出的函数
  • 它修改PyTypeObject实例本身。

因此,要复制它,需要对PyTypeObject类型本身进行黑客攻击。

如果你想这样做,最容易尝试的事情可能是(暂时的?)在type->tp_flags & Py_TPFLAGS_HEAPTYPE str 类上设置。,这将允许正常设置属性。当然,不能保证这不会使您的解释器崩溃.

这不是我想要做的(特别是通过ctypes),除非我真的必须这样做,所以我给你提供了一个捷径。

你写:

那么,如何才能得到预期的行为:‘'foo’'bar‘

这实际上很容易使用sys.displayhook

在计算在交互式Python会话中输入的sys.displayhook的结果时调用表达式。可以通过向sys.displayhook分配另一个参数函数来自定义这些值的显示。

下面是一个例子:

代码语言:javascript
复制
import sys

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

sys.displayhook = displayhook

然后..。(!)

代码语言:javascript
复制
'foo'
#>>> 'bar'

123
#>>> 123

关于为什么要缓存repr的哲学观点,首先要考虑:

代码语言:javascript
复制
1 + 1

如果在调用之前必须先在字典中查找__add__,CPython会很慢,所以CPython决定缓存对标准dunder (双下划线)方法的查找。__repr__就是其中之一,即使不太常见也需要优化查找。这对于保持格式化('%s'%s)的速度仍然很有用。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26061351

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档