Python:类型和数据类型之间的混淆

Python:类型和数据类型之间的混淆,python,numpy,types,unsigned-integer,Python,Numpy,Types,Unsigned Integer,假设我进入: a = uint8(200) a*2 然后结果是400,它被重铸为uint16类型 然而: a = array([200],dtype=uint8) a*2 结果是 array([144], dtype=uint8) 乘法以256模执行,以确保结果保留在一个字节内 我对“类型”和“数据类型”以及其中一种类型优先于另一种类型感到困惑。正如您所看到的,类型可能会对输出产生显著的影响 例如,我是否可以创建一个数据类型uint8,以便对该数字执行256模运算?或者,我可以创建一个ui

假设我进入:

a = uint8(200)
a*2
然后结果是400,它被重铸为uint16类型

然而:

a = array([200],dtype=uint8)
a*2
结果是

array([144], dtype=uint8)
乘法以256模执行,以确保结果保留在一个字节内

我对“类型”和“数据类型”以及其中一种类型优先于另一种类型感到困惑。正如您所看到的,类型可能会对输出产生显著的影响


例如,我是否可以创建一个数据类型uint8,以便对该数字执行256模运算?或者,我可以创建一个uint8类型(不是dtype)的数组,这样对它的操作将产生0-255范围以外的值吗?

一个numpy数组包含相同类型的元素,因此
np。数组([200],dtype=uint8)
是一个具有一个
uint8类型值的数组。如果执行
np.uint8(200)
,则没有数组,只有一个值。这将产生巨大的不同


在数组上执行某些操作时,无论单个值是否溢出,类型都保持不变。禁止在阵列中自动向上投射,因为整个阵列的大小必须改变。只有当用户明确希望这样做时,才会这样做。当对单个值执行操作时,它可以很容易地向上转换,而不会影响其他值。

NumPy数组的
类型是
NumPy.ndarray
;这就是Python对象的类型(类似于
type(“hello”)
就是
str


dtype
只定义了如何用标量(即单个数字)或数组解释内存中的字节,以及处理字节的方式(例如
int
/
float
)。因此,您不会更改数组或标量的
type
,只更改其
dtype

正如您所观察到的,如果将两个标量相乘,则生成的数据类型是两个值都可以转换到的最小“安全”类型。但是,将数组与标量相乘只会返回相同数据类型的数组。函数
np.inspect\u types
的属性可以清楚地了解特定标量或数组对象的
dtype
何时更改:

NumPy的P>类型推广与C++语言中的规则相似,但也有一些细微的差别。同时使用标量和数组时,数组的类型优先,并考虑标量的实际值

文件继续:

如果只有标量或标量的最大类别高于数组的最大类别,则数据类型将与
promote\u types
组合以生成返回值

因此,对于
np.uint8(200)*2
,两个标量,得到的数据类型将是以下返回的类型:

对于
np.array([200],dtype=np.uint8)*2
数组的数据类型优先于标量
int
,并返回
np.uint8
数据类型

要解决关于在操作期间保留标量的
dtype
的最后一个问题,您必须限制您使用的任何其他标量的数据类型,以避免NumPy的自动
dtype
升级:

>>> np.array([200], dtype=np.uint8) * np.uint8(2)
144
当然,另一种选择是简单地将单个值包装在NumPy数组中(这样NumPy就不会在使用不同的
dtype
标量的操作中强制转换它)

要在操作期间提升数组的类型,可以先在数组中包装任何标量:

>>> np.array([200], dtype=np.uint8) * np.array([2])
array([400])

简单而高级的答案是,NumPy在Python的类型系统之上分层了第二个类型系统

当您询问NumPy对象的
类型时,您会得到容器的类型——类似于
NumPy.ndarray
。但是当您请求
dtype
时,您得到的是元素的(numpy-managed)类型

在其他情况下,没有等效的Python类型。例如,当您指定
uint8
时。这样的数据值/类型可以由Python管理,但与C、Rust和其他“系统语言”不同,管理直接与机器数据类型对齐的值(如
uint8
与“无符号字节”计算紧密对齐)不是Python的常见用例

所以最大的问题是NumPy提供了数组和矩阵之类的容器,它们在自己的类型系统下运行。它还提供了一系列非常有用、优化良好的例程来操作这些容器(及其元素)。如果使用care,您可以混合和匹配numpython和普通Python计算

没有Python类型
uint8
。有一个名为
uint8
的构造函数,调用该构造函数时返回NumPy类型:

>>> u = uint8(44)
>>> u
44
>>> u.dtype
dtype('uint8')
>>> type(u)
<type 'numpy.uint8'>

但如果您想计算mod 256的值,那么使用Python的mod运算符可能更容易:

>> (44 + 1000) % 256
20


将大于255的数据值驱动到
uint8
数据类型中,然后执行算术是获得mod-256算术的一种相当隐蔽的方法。如果您不小心,您可能会导致Python将您的值“升级”为全整数(杀死您的mod-256方案),或者触发溢出异常(因为在C和机器语言中工作良好的技巧通常会被更高级的语言标记)。

dtype
只定义了内存中的字节如何被标量解释。”→ 它还定义了它们的解释方式(例如,
int32
vs
float32
)。
>>> arr.dtype == float
True
>>> arr.dtype is float
False
>>> u = uint8(44)
>>> u
44
>>> u.dtype
dtype('uint8')
>>> type(u)
<type 'numpy.uint8'>
>>> uint8(44 + 1000)
20
>>> uint8(44) + uint8(1000)
20
>> (44 + 1000) % 256
20