Como testar se PyObject possui um iterador

8

Estou implementando uma função C como uma extensão para Python. Lá dentro abstract.h, encontrei o seguinte:

/* ==== Iterators ================================================ */

/* Takes an object and returns an iterator for it.
   This is typically a new iterator but if the argument is an iterator, this
   returns itself. */
PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

/* Returns 1 if the object 'obj' provides iterator protocols, and 0 otherwise.

   This function always succeeds. */
PyAPI_FUNC(int) PyIter_Check(PyObject *);

Quando tento fazer com que os iteradores usem PyObject_GetIterobjetos obviamente não iteráveis ​​como um número, recebo o erro SystemError: <built-in function xxx> returned a result with an error set.

static PyObject *my_method(PyObject *self, PyObject *args) 
{
    PyObject *obj;
    PyArg_ParseTuple(args, "O", &obj)
    //  printf("\ncheck %d",PyIter_Check(obj)); // always 0
    PyObject *iter = PyObject_GetIter(obj); // throws error
    return PyLong_FromLong(0);
}

Eu gostaria de lidar com os erros sozinho. Então, tentei usar o PyIter_Checkpara testar se o objeto tem um iterador. No entanto, essa função retornou 0 para todos os objetos que forneci, incluindo os iteráveis.

Eu pensei que isso poderia ser causado pela PyAPI_FUNC()macro, mas eu a encontrei pyport.he parece estar apenas adicionando __declspec.

  • Por que a função está PyIter_Checkretornando zero para todos os objetos?
thehorseisbrown
fonte

Respostas:

2

PyIter_Checké para verificar se um objeto é um iterador, não se ele pode fornecer um. Não parece haver um PyIterable_Check.

Além disso, o Python praticamente aplica o EAFP: como qualquer coisa pode fornecer um __iter__que gera uma exceção, você deve procurar um erro de PyObject_GetIterqualquer maneira, portanto, o único ponto das *_Checkfunções é fornecer verificações antecipadas de sanidade (às vezes com melhores mensagens de erro).

Davis Herring
fonte
Obrigado! Super útil. Vou estudar o tratamento de exceção :)
thehorseisbrown
@thehorseisbrown: Além de Py_DECREFs apropriados para referências de propriedade existentes, geralmente você pode apenas dizer PyObject *const x=Py_Foo(…); if(!x) return 0;.
Davis Herring