当然,没有办法从它的 repr 中获取一个对象,因为任何类都可以在它的 repr 中返回它想要的任何东西。但是在 repr 中包含指针的特定情况下,您基本上是在询问如何从指针中获取对象。哪个(至少在 C Python 中)与询问如何从其 id 获取对象完全相同。
没有内置的方法可以做到这一点,尽管它很简单。这是故意的,因为这几乎总是一个坏主意。
如果您认为通过 id 获取对象有实际目的,那么您可能错了。特别是因为您必须处理通常自动处理的对象生命周期问题(并且在您背后,这使得即使您尝试也很难手动处理它们)。例如,如果一个对象消失了,该对象的一个弱引用将变为空,但 id 仍指向释放的内存,或稍后创建的另一个对象,或一个对象的一半和另一个对象的一半。当然,你在 C Python 中所做的任何事情都不会在 PyPy 或 jython 或 IronPython 中工作......
但是,如果您只是想了解 C Python 运行时的工作原理,那么这是一个合理的问题,并且有一个合理的答案。可能最好的方法是创建一个 C 扩展模块。如果你不知道如何创建扩展模块,你真的应该先学习,然后再回到这个问题。如果你这样做了,你想要实现的功能就很简单了:
static PyObject *objectFromId(PyObject *self, PyObject *args) {
PyObject *obj;
if (!PyArg_ParseTuple(args, "n", &obj)) return NULL;
Py_INCREF(obj);
return obj;
}
如果您愿意,您可以在 Pyrex/Cython 中而不是 C 中执行此操作。或者您可以直接在 _ctypes 模块上调用 PyObj_FromPtr。但是,如果您想了解这个级别的工作原理,那么明确发生的事情并明确地将“处理 C 指针”代码放在 C 中会更有意义。
进一步考虑,如果您想构建一种最佳猜测 objectFromRepr 用于修补目的,您可以。基本上:
- 使用匹配常见尖括号形式的正则表达式;如果匹配,则使用地址调用 objectFromId。
- 调用评估。
您可能希望在其中放置一个中间步骤:对于许多类型,repr 采用构造函数调用的形式,例如 Class(arg1, arg2),在这种情况下,最好将该形式与另一个匹配正则表达式并直接调用构造函数,而不是使用 eval。如果不出意外,它可能更有启发性,这就是本练习的重点,对吧?
显然,在实际代码中,这比 objectFromId 更糟糕。使用 eval 几乎总是不好的,并且 eval(repr(x)) == x 实际上并不能保证为真,并且您实际上在 id 情况下获得了对同一对象的新引用,但新对象带有eval 情况下的值相同,并且…
PS,在您了解了所有这些在 C Python 中的工作原理之后,可能值得在另一个解释器(如 jython 或 IronPython)中进行类似的练习(尤其是当您表示本机 Java/.NET/etc. 对象时)。