1、如何获取numpy数组元素的真实地址?
在Python编程中,numpy是一个很好用的扩展程序库,将其与SciPy库和 Matplotlib绘图库一起使用,可构成一个强大的类似于Matlab的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。在Python中,当你定义了一个numpy类型的数组后,它内部元素的真实地址如何获得呢?
![27de70ca7d9fe5ca18f16a2fc1edf64b.png](https://i-blog.csdnimg.cn/blog_migrate/126f39b4886e45c56ba7b24ad5b93152.png)
这里可以通过numpy数组的“__array_interface__”接口得到。
比如下面的例子:
import numpy as np
from ctypes import *
a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)
print(a.__array_interface__['data'])
上面程序定义了一个numpy类型的数组a,运行后得到:
(1883707159072, False)
接口“__array_interface__”返回一个包含两个元素的元组,其第1个元素即为a数组内部元素存放的真实地址,第2个元素标明了该数组是否为“只读”属性。
2、如何与ctypes库创建的数组共享内存空间?
对于Python编程人员来说,ctypes库也是使用率比较高的一个库,当调用第三方提供的动态库链接库函数时,它经常用于定义与C语言兼容的数据类型变量,作为Python语言与C语言进行数据交互的桥梁。
![9786dd7298193785125fe3fa53bd601b.png](https://i-blog.csdnimg.cn/blog_migrate/92511e7766b5d02c92f44a57da426d37.png)
那么,如何使用ctypes库定义一个与numpy共享内存空间的数组变量呢?
仍以上面的例子,定义一个uint8类型的数组b,与a数组共享内存区域,可使用下面的代码:
b = (c_uint8*len(a)).from_address(a.__array_interface__['data'][0])
print('a =', a, 'nb =', b[:])
运行后得到:
a = [1 2 3 4 5]
b = [1, 2, 3, 4, 5]
可见,使用ctypes库的from_address函数定义了一个与a同地址的数组b,打印出b的元素值后,其初始元素值确实与数组a相同。
下面验证一下,数组a和b里面的元素是否真正的占用同一个内存区域。
先改变数组a中第1个元素的值,运行下面的代码:
a[0] = 10
print('a =', a, 'nb =', b[:]}])
其运行结果为:
a = [10 2 3 4 5]
b = [10, 2, 3, 4, 5]
可见,将数组a中的第一个元素的值由1改成10后,b数组中第一个元素也同步改变成10了。
紧接着,再改变b数组中第二个元素的值,由2改为20:
b[1] = 20
print('a =', a, 'nb =', b[:])
其运行结果为:
a = [10 20 3 4 5]
b = [10, 20, 3, 4, 5]
显然,数组a中第二个元素的值也同步改变为20了。
因此,数组a与数组b确实是占用的同一个内存地址空间。
另外,也可以通过数组b再定义一个与b共享内存区域的numpy数组c,使用下面的代码即可:
c = np.frombuffer(b, dtype=np.uint8)
可以自行验证数组a、b、c是否为同一个内存空间。
3、结论
上面的例子表明,在Python编程中,numpy类型的数组与ctypes类型的数组可以进行互换,比如,可以使用numpy库进行复杂的运算后,再转换成ctypes类型的数组,传递到第3方的动态库dll函数中作进一步的处理,这样,在某些场合下,既能使用numpy库的强大运算功能,又可以使用第三方库,来实现我们特定的处理需求。