2016-10-04 4 views
9

Ich möchte eine Funktion in C erstellen, die Python erweitert, die Eingaben vom Typ float oder int annehmen kann. Also im Grunde möchte ich f(5) und f(5.5) zu akzeptablen Eingaben sein.Wie kann ich eine C-Funktion schreiben, die entweder ein int oder ein float nimmt?

Ich glaube nicht, dass ich if (!PyArg_ParseTuple(args, "i", $value)) verwenden kann, weil es nur nur int oder nur float dauert.

Wie kann ich zulassen, dass meine Funktion Eingaben zulässt, die entweder Ints oder Floats sind?

Ich frage mich, ob ich nur die Eingabe nehmen und es in ein PyObject und irgendwie den Typ des PyObject nehmen sollte - ist das der richtige Ansatz?

+0

Hinweis: Ich habe Ihre Frage bearbeitet, um das Lesen zu erleichtern. Wenn du etwas nicht magst, das ich geändert habe, dann kannst du es gerne ändern, weil es deine Frage ist, nicht meine. – immibis

+1

Sie möchten wahrscheinlich mit 'PyObject' arbeiten und dann das [Number Protocol] (https://docs.python.org/2/c-api/number.html) verwenden, um mit den Zahlen zu arbeiten. Wenn Sie zum Beispiel auf einen 'float' zwingen wollen, können Sie' PyNumber_Float' auf Ihrem 'PyObject' aufrufen. – mgilson

Antwort

5

Wenn Sie eine C-Funktion zum Akzeptieren von Gleitkommazahlen deklarieren, wird sich der Compiler nicht beschweren, wenn Sie ihm einen int übergeben. Zum Beispiel erzeugt das Programm die Antwort 2.000000:

#include <stdio.h> 

float f(float x) { 
    return x+1; 
} 

int main() { 
    int i=1; 
    printf ("%f", f(i)); 
} 

eine Python-Modul-Version, iorf.c:

#include <Python.h> 

static PyObject *IorFError; 

float f(float x) { 
    return x+1; 
} 


static PyObject * 
fwrap(PyObject *self, PyObject *args) { 
    float in=0.0; 
    if (!PyArg_ParseTuple(args, "f", &in)) 
    return NULL; 
    return Py_BuildValue("f", f(in)); 
} 

static PyMethodDef IorFMethods[] = { 
    {"fup", fwrap, METH_VARARGS, 
    "Arg + 1"}, 
    {NULL, NULL, 0, NULL}  /* Sentinel */ 
}; 


PyMODINIT_FUNC 
initiorf(void) 
{ 
    PyObject *m; 

    m = Py_InitModule("iorf", IorFMethods); 
    if (m == NULL) 
    return; 

    IorFError = PyErr_NewException("iorf.error", NULL, NULL); 
    Py_INCREF(IorFError); 
    PyModule_AddObject(m, "error", IorFError); 
} 

Die setup.py:

from distutils.core import setup, Extension 

module1 = Extension('iorf', 
        sources = ['iorf.c']) 

setup (name = 'iorf', 
     version = '0.1', 
     description = 'This is a test package', 
     ext_modules = [module1]) 

Ein Beispiel:

03:21 $ python 
Python 2.7.10 (default, Jul 30 2016, 18:31:42) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import iorf 
>>> print iorf.fup(2) 
3.0 
>>> print iorf.fup(2.5) 
3.5 
+0

Aber nur weil der C-Compiler das 'int' in ein' float' * konvertiert hat, bevor * die Funktion aufgerufen hat. –

+0

Ist das ein Problem? –

+0

Ich glaube nicht, dass die Python-Laufzeit das Gleiche tun wird. –

1

Sie können Art von Eingabewert wie folgt überprüfen:

PyObject* check_type(PyObject*self, PyObject*args) { 
    PyObject*any; 

    if (!PyArg_ParseTuple(args, "O", &any)) { 
     PyErr_SetString(PyExc_TypeError, "Nope."); 
     return NULL; 
    } 

    if (PyFloat_Check(any)) { 
     printf("indeed float"); 
    } 
    else { 
     printf("\nint\n"); 
    } 

    Py_INCREF(Py_None); 

    return Py_None; 
} 

Sie Float-Wert von Objekt extrahieren kann mit:

double result=PyFloat_AsDouble(any); 

Aber in dieser besonderen Situation wahrscheinlich keine Notwendigkeit, dies zu tun, nein egal, was Sie analysieren int oder float Sie können es als Schwimmer nehmen und auf Rundheit überprüfen:

float target; 
    if (!PyArg_ParseTuple(args, "f", &target)) { 
       PyErr_SetString(PyExc_TypeError, "Nope."); 
       return NULL; 
    } 

    if (target - (int)target) { 
     printf("\n input is float \n"); 
    } 
    else { 
     printf("\n input is int \n"); 
    } 
1

Floats werden (normalerweise) via übergeben Register während Ints (normalerweise) über den Stack übergeben werden. Das bedeutet, dass Sie innerhalb der Funktion nicht überprüfen können, ob das Argument ein float oder ein int ist.

Die einzige Problemumgehung besteht darin, Variadic-Argumente zu verwenden, wobei das erste Argument den Typ entweder als int oder double (nicht float) angibt.

Obwohl ich nicht sicher bin, ob Python mit variadischen Funktionen umgehen kann, also YMMV. Als letzte Grabenbemerkung können Sie den Wert immer in einen Puffer sprinten und Ihre Funktion sscanf float/int aus dem Puffer lassen.

Verwandte Themen