代码之家  ›  专栏  ›  技术社区  ›  Tyler Weiss

如何获取指向通用PyObject*内部数据的指针?

  •  1
  • Tyler Weiss  · 技术社区  · 3 年前

    我正试图获取PyObject*(具体来自Python.h 3.8.2)中保存的数据的内存地址,这样我就可以对缓冲区执行memcpy操作。我只知道如何将数据从对象中复制出来,但只知道如何获取指针。假设我有这个对象 data ...

    PyObject* data = PyLong_FromLong(100L);
    

    到目前为止,我将这些数据转移到缓冲区的唯一选择似乎是将其复制出来,然后使用临时变量的地址进行memcpy。。。

    long temp = PyLong_AsLong(data);
    memcpy(buffer, &temp, 8);
    

    这已经做了成千上万次了,所以我想如果我能得到数据的内存地址并直接将其复制到我的缓冲区,速度会更快,比如。。。

    memcpy(buffer, data->address_to_data(), 8)
    

    而不是拥有临时变量的额外副本。

    有人知道我是否/如何从PyObject*包装器中获取长值的内存地址吗?

    感谢您的帮助!

    0 回复  |  直到 3 年前
        1
  •  3
  •   DavidW    3 年前

    这看起来像是一个X-Y问题(即,您认为需要从C级的一堆Python对象中提取数据,但实际上您将受益于拥有一个暴露 全部的 您的数据)。

    Python int 可以存储(几乎)任意大的数字:

    >>> 1000**1000  # creates a very big int
    

    即,它不作为C长度存储在内部。 Internally it is stored 作为整数数组( ob_digits )的大小 ob_size 它们的格式有点奇怪,对你没有多大用处。但是,如果您真的想复制它,您可以将对象指针大小写为 PyLongObject* 然后做一个 memcpy(&dest, my_int->ob_digit, sizeof(digit)*abs(my_int->ob_size)); 。我建议不要这样做,因为你很难使用这些数据。

    显然,只有当你知道你有一个Python时,这才适用 int .对于“通用” PyObject* “这不起作用,因为 PyObject* 可以包含几乎任何数据。这包括需要所有权和/或引用计数的指针(这尤其适用于任何 PyObject 包含其他 PyObject s


    我认为您实际想要的是将数据存储在一个C整数的大数组中。这可以通过 array.array numpy.array ,或各种其他类。

    在C级别上,这些对象支持 the buffer protocol 在那里,它们将该内部数组暴露给C,允许从C访问、复制、操作等每个值。

    一些未经测试的快速说明性代码:

    Py_Buffer view;
    view.format = "l"; // request an array of longs
    if (PyObject_GetBuffer(obj, &view, PyBUF_CONTIG | PyBUF_FORMAT | PyBUF_WRITABLE ) == -1) {
       // failed
       return NULL;
    }
    
    // you want to check that view.ndim == 1 (for a simple 1D array)
    long* data = (long*)view.buf;
    // At this point you can access data as a C array of length view.len
    
    // When you've finished;
    PyBuffer_Release(view);
    
        2
  •  0
  •   moi    3 年前

    这似乎是一个与数据结构抽象相关的设计问题。通常,希望给用户提供不透明的数据结构或指针。访问内部元素需要一个方法(或函数)调用。

    从…起 https://docs.python.org/3/c-api/long.html ,

    PyObject* PyLong_FromLong(long v)
    
    Return value: New reference.
    Return a new PyLongObject object from v, or NULL on failure.
    
    The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.
    

    该调用可能正在对PyLongObject进行内部转换,PyLongObject可以是指向其内部对象的链接。如果您在-5到256之间传递,它将用其内部对象替换您的整数。对于其他对象,将创建一个新对象。即使找到了内部内存位置,也不能保证行为保持一致。

    PyObject的设计似乎是不透明的。这样对待它。

        3
  •  0
  •   unddoch    3 年前

    有一个内部CPython函数,它可以执行您想要的操作,称为 _PyLong_AsByteArray .

    它似乎从一个名为 ob_digit ,但我并没有完全理解整个函数。