I'd restructure this routine as follows: Use a plain C array for storing the temporary argument list for ordering. Using Python lists here is not efficient (PySequence_* are generic methods and go through a longer code path), and moreover you end up doing a lot of unnecessary reference counting.

```
int num_args;
if (!PyTuple_Check(args)) {
    PyErr_SetString(...);
    return NULL;
}
working_args = (PyObject*)malloc(sizeof(PyObject*) * PyTuple_GET_SIZE(args));
nargs = 0;
for (i = 0; i < nargs; ++i) {
    if (PyArray_CheckExact(obj) || ...) { 
        continue; 
    }
    if (PyObject_HasAttrString(obj, "__numpy_ufunc__")) {
        working_args[nargs] = obj;
        ++nargs;
    }
}

/* 
   Then process working_args according to the scheme @njsmith and I suggested.
   After making a call for one of them, you can set the corresponding entry in working_args to NULL
   to indicate it's out of the game now.
  */

/* And when exiting: */
free(working_args);
```