如何在Python中重写str子类的ord行为?
我有一个这样的类:
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
!不过,如果你只需要在一个独立的模块中使用,这也是一种可行的办法。