如何在Python中重写str子类的ord行为?

2 投票
4 回答
954 浏览
提问于 2025-04-15 16:57

我有一个这样的类:

class STR(str):

    def __int__(self):
        return 42 

如果我在提示中这样使用它:

>>> a=STR('8')
>>> ord(a)
56
>>> int(a)
42
>>> chr(a)
'*'

这就是它的表现。我希望ord(a)的结果是42。我该怎么做呢?我应该重写str类中的哪个方法?这些内容有文档说明吗?

谢谢!

4 个回答

0

你不能直接改变ord这个函数,但你可以把它当成一个变量来重新赋值,比如说:

先备份原来的ord

origord= ord

def ord(x):
    return 42


ord(a)
42

origord(a)
56

不过我觉得这样做并不好。

2

有人已经发布了内置的 ord 代码,而且你无法拦截任何方法调用。

一个解决方案是重写 ord 函数,比如:

backup_ord = ord
def ord(obj):
    if hasattr(obj, '__ord__'):
        return obj.__ord__()
    else:
        return backup_ord(obj)

然后你可以定义一个类,并在里面写上 __ord__ 方法,做一些类似这样的事情:

class MyStr(str):
    def __ord__(self):
        return 'LOL'

测试代码:

normal_five = '5'
strange_five = MyStr('5')
print ord(normal_five)
print ord(strange_five)

输出结果:

53
LOL
3

这里是Python内置的 ord 函数的C语言源代码,来自于当前的bltinmodule.c版本

static PyObject *
builtin_ord(PyObject *self, PyObject* obj)
{
    long ord;
    Py_ssize_t size;

    if (PyString_Check(obj)) {
        size = PyString_GET_SIZE(obj);
        if (size == 1) {
            ord = (long)((unsigned char)*PyString_AS_STRING(obj));
            return PyInt_FromLong(ord);
        }
    } else if (PyByteArray_Check(obj)) {
        size = PyByteArray_GET_SIZE(obj);
        if (size == 1) {
            ord = (long)((unsigned char)*PyByteArray_AS_STRING(obj));
            return PyInt_FromLong(ord);
        }

#ifdef Py_USING_UNICODE
    } else if (PyUnicode_Check(obj)) {
        size = PyUnicode_GET_SIZE(obj);
        if (size == 1) {
            ord = (long)*PyUnicode_AS_UNICODE(obj);
            return PyInt_FromLong(ord);
        }
#endif
    } else {
        PyErr_Format(PyExc_TypeError,
                 "ord() expected string of length 1, but " \
                 "%.200s found", obj->ob_type->tp_name);
        return NULL;
    }

    PyErr_Format(PyExc_TypeError,
             "ord() expected a character, "
             "but string of length %zd found",
             size);
    return NULL;
}

你可以看到,它并没有对你传入的实例调用任何方法。根据我的理解,如果你没有传入一个明确的字符串,就没有办法改变 ord 的功能。

实际上,它主要是在检查这个PyObject是不是字符串、字节数组或者Unicode——这就是PyString_Check等函数的作用。如果都不是,你就会遇到TypeError错误。

有一种解决方法,但不太适合大规模使用,那就是在全局命名空间里自己写一个 ord 函数:

>>> class STR(str):
...     def __int__(self):
...             return 42
... 
>>> 
>>> def ord(s):
...     if isinstance(s, STR):
...             return int(s)
...     else:
...             return __builtins__.ord(s)
... 
>>>  
>>> ord(STR('fdsafds'))
42
>>> ord("!")
33

当然,这种方法扩展性很差,因为其他模块可能会直接调用 __builtins__.ord,或者他们自己可能会覆盖 ord!不过,如果你只需要在一个独立的模块中使用,这也是一种可行的办法。

撰写回答