我需要编写一个函数,该函数根据python参数是否可迭代来执行不同的操作。这是一个代码示例:
PyObject *iter = PyObject_GetIter(arg); // arg is a PyObject*
if (iter) {
// do iterable things
} else {
// do non-iterable things
}
但是,如果arg
不可迭代,则不仅iter == NULL
还会出现异常。处理这种情况的正确方法是什么?我只是打电话给PyErr_Clear()
并希望没有其他错误设置吗?
答案 0 :(得分:1)
在纯Python中,找出对象是否可迭代的通常解决方案是调用iter(...)
并查看会发生什么情况(例如,它被“ Fluent Python”流行):
def is_iterable(obj):
try:
iter(obj) # ok, it worked
return True
except TypeError:
return False
有关更多详细信息,请参见出色的answer。
这基本上也是@falsetru在注释中的建议-如果PyObject_GetIter
失败,则尝试清除错误:
int is_iterator(PyObject *obj){
PyObject *it = PyObject_GetIter(obj);
if(it != NULL){
Py_DECREF(it);
return 1; // object can be iterated
}
else if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
return 0; // is not an iterator
}
else{
return -1; // error
}
}
但这并不是“可迭代”的意思,然后可以根据需要调整PyObject_GetIter
的实现,例如:
int is_iterator2(PyObject *obj) {
return Py_TYPE(obj)->tp_iter != NULL || PySequence_Check(obj);
}
与通常的算法一样,is_iterator2
会查询是否存在tp_iter
插槽(即__iter__
函数),如果不存在,则会通过{退回到sequence protocol {1}}。但是,与第一个版本不同的是,未调用__getitem__
槽,并且不检查其结果是否是迭代器,即用于
tp_iter
class C:
def __iter__(self):
raise BufferError()
class D:
def __iter__(self):
return 1; # isn't iterator
和C()
将被归类为可迭代(对于第一个D()
版本则不是这种情况)。同样,如果is_iterator
返回1,则并不意味着is_iterator2
不返回PyObject_GetIter
,如上述类所示。