快速检查Python3整数是否适合C long或int

问题描述 投票:0回答:1

在 Python2 的 C API 中,有

long PyInt_AS_LONG()
函数,可以从 Python2
long
快速创建 C
int

是否有一种快速方法可以使用 Python3 C API 检查

int
(实际上是任意精度整数)是否适合 C
long

可以调用

PyLong_AsLong()
,但这似乎做了不必要的工作。应该有一个函数来获取用于存储数字的内存块的大小,但我在文档中找不到任何内容。

这必须用C完成,所以我不能直接使用Python算术比较。虽然原则上我可以调用 PyObject_RichCompare(),但它看起来有点矫枉过正。

python python-3.x python-c-api
1个回答
0
投票

PyLong_AsLong代码可以在[GitHub]找到:python/cpython - (v3.10.13) cpython/Objects/longobject.c#453并且(正如@jasonharper在评论中指出的那样)使用PyLong_AsLongAndOverflow(其中就在它上面)。

我创建了一个测试程序。

_mod00.c:

#define PY_SSIZE_T_CLEAN
#include "Python.h"

#define MOD_NAME "_mod00"


static PyObject* PyTestAsLongAndOverflow(PyObject *self, PyObject *iObj)
{
    int of;
    if (!PyLong_CheckExact(iObj)) {
        Py_RETURN_FALSE;
    }
    PyLong_AsLongAndOverflow(iObj, &of);
    if (of) {
        Py_RETURN_FALSE;
    }
    Py_RETURN_TRUE;
}


static PyObject* PyTestAsLong(PyObject *self, PyObject *iObj)
{
    if (!PyLong_CheckExact(iObj)) {
        Py_RETURN_FALSE;
    }
    PyLong_AsLong(iObj);
    if (PyErr_Occurred()) {
        PyErr_Clear();
        Py_RETURN_FALSE;
    }
    Py_RETURN_TRUE;
}


static PyObject* PyTestParseTuple(PyObject *self, PyObject *args)
{
    long l = 0;
    if (!PyArg_ParseTuple(args, "l", &l)) {
        PyErr_Clear();
        Py_RETURN_FALSE;
    }
    Py_RETURN_TRUE;
}


static PyMethodDef moduleMethods[] = {
    {"test_as_long_and_overflow", (PyCFunction)PyTestAsLongAndOverflow, METH_O, NULL},
    {"test_as_long", (PyCFunction)PyTestAsLong, METH_O, NULL},
    {"test_parse_tuple", (PyCFunction)PyTestParseTuple, METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL},  // Sentinel
};

static struct PyModuleDef moduleDef = {
    PyModuleDef_HEAD_INIT, MOD_NAME, NULL, -1, moduleMethods
};


PyMODINIT_FUNC PyInit__mod00(void)
{
    return PyModule_Create(&moduleDef);
}

code00.py

#!/usr/bin/env python

import sys
import timeit

import _mod00 as mod


FUNCS = (
    mod.test_as_long_and_overflow,
    mod.test_as_long,
    mod.test_parse_tuple,
)


def test_acc():
    print("Test accuracy:")
    values = (
        None,
        False,
        3.141593,
        "",
        -1,
        0,
        1,
        sys.maxsize,
        sys.maxsize + 1,
        -sys.maxsize,
        -sys.maxsize - 1,
        -sys.maxsize - 2,
    )
    for value in values:
        print("  ({:s}) {:}{:s}:".format(value.__class__.__name__, value, " (hex: {:016X})".format(value) if isinstance(value, int) else ""))
        for func in FUNCS:
            print("    {:s}: {:}".format(func.__name__, func(value)))


def test_perf(count=10000000):
    print("\nTest performance:")
    values = (
        sys.maxsize,
        sys.maxsize + 1,
    )
    for value in values:
        print("  ({:s}) {:}{:s}:".format(value.__class__.__name__, value, " (hex: {:016X})".format(value) if isinstance(value, int) else ""))
        for func in FUNCS:
            print("    {:s}: {:.3f}s".format(func.__name__, timeit.timeit(lambda: func(value), number=count)))


def main(*argv):
    test_acc()
    test_perf()


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

输出

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q076889790]> . ~/sopr.sh 
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  _mod00.c
[064bit prompt]> 
[064bit prompt]> gcc -fPIC -I/usr/include/python3.10 -shared -o _mod00.so _mod00.c
[064bit prompt]> ls
code00.py  _mod00.c  _mod00.so
[064bit prompt]> 
[064bit prompt]> python ./code00.py 
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] 064bit on linux

Test accuracy:
  (NoneType) None:
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: False
  (bool) False (hex: 0000000000000000):
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: True
  (float) 3.141593:
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: False
  (str) :
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: False
  (int) -1 (hex: -000000000000001):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) 0 (hex: 0000000000000000):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) 1 (hex: 0000000000000001):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) 9223372036854775807 (hex: 7FFFFFFFFFFFFFFF):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) 9223372036854775808 (hex: 8000000000000000):
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: False
  (int) -9223372036854775807 (hex: -7FFFFFFFFFFFFFFF):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) -9223372036854775808 (hex: -8000000000000000):
    test_as_long_and_overflow: True
    test_as_long: True
    test_parse_tuple: True
  (int) -9223372036854775809 (hex: -8000000000000001):
    test_as_long_and_overflow: False
    test_as_long: False
    test_parse_tuple: False

Test performance:
  (int) 9223372036854775807 (hex: 7FFFFFFFFFFFFFFF):
    test_as_long_and_overflow: 0.819s
    test_as_long: 0.828s
    test_parse_tuple: 1.140s
  (int) 9223372036854775808 (hex: 8000000000000000):
    test_as_long_and_overflow: 0.813s
    test_as_long: 1.128s
    test_parse_tuple: 1.587s

Done.

注释

© www.soinside.com 2019 - 2024. All rights reserved.