asarray 遇到已经是数组的输入,直接返回它自己——省内存,但也埋了个坑。 array 一定复制一份,改副本不影响原数组。 函数入口用 asarray 统一输入类型,函数内部要改就先 copy。
上一篇造的是新数据。这一篇处理「手上已经有一个数组」的情况。 区别只有一句话:复制还是不复制。但后果能拖一整晚。
一、asarray 不复制
B = np.asarray(A) 之后,A 和 B 指向同一块内存。
B is A 给 True,B.base 指向 A——说明 B 是 A 的视图。
asarray 遇到已经是 ndarray 的输入,直接返回它自己。省一次复制。
输入是列表呢?那就得新建,照样占内存。
二、视图的坑:改一个,另一个也变
B[0] = 99 之后,A 的第 0 个也变成 99 了。
因为 B 和 A 是同一块内存的两个名字。
换成 C = np.array(A) 再改 C,A 就不动了——array 复制了一份。
要独立就用 array。代价是多占一份内存,好处是互不干扰。
三、什么时候用哪个
| 情况 | 用哪个 | 为什么 |
|---|---|---|
| 输入可能不是数组 | asarray | 是的话直接返回,省一次复制 |
| 要改且不能影响原数据 | array | 复制一份 |
| 只是读一下 | asarray | 更快、更省内存 |
一个常见写法:A = np.asarray(可能是列表也可能是数组的东西),把输入统一。
函数入口用 asarray,函数内部要改就先 copy。这条能防住一多半「莫名其妙被改了」。
四、从别的结构来
从元组:np.array((1, 2, 3)),和列表一样。
从生成器:np.fromiter((x*x for x in range(4)), int)。
从字符串:np.array('1 2 3'.split(), dtype=int),先 split 再转。
fromiter 必须显式给 dtype。不给的话它没法推,直接报错。
五、动手 Demo:从已有数组创建
先验证 asarray 和 array 的复制差异,再演示改视图会连带改原数组。 最后从元组、生成器、字符串各造一个。
import numpy as np
A = np.array([1, 2, 3])
B = np.asarray(A)
C = np.array(A)
print("A is B:%s A is C:%s" % (A is B, A is C))
B[0] = 99
print("改 B 之后 A = %s" % A)
A = np.array([1, 2, 3])
C = np.array(A)
C[0] = 99
print("改 C 之后 A = %s,C = %s" % (A, C))
print("从元组 %s" % np.array((1, 2, 3)))
print("从生成器 %s" % np.fromiter((x * x for x in range(4)), int))
print("从字符串 %s" % np.array("1 2 3".split(), dtype=int))
实跑输出:
--------------------------------------------------------------------
从已有数组创建
--------------------------------------------------------------------
① asarray 不复制,array 会复制
--------------------------------------------------------------------
A 和 B 是不是同一块内存:True。
A 和 C 是不是同一块内存:False。
但 B.base 指向 A,说明 B 是 A 的「视图」,不是新数组。
! asarray 遇到已经是 ndarray 的输入,直接返回它自己。省内存,但也埋了个坑。
② 视图的坑:改一个,另一个也变
--------------------------------------------------------------------
改 B 的第 0 个之后,A 变成 [99 2 3]。
因为 B 和 A 指的是同一块内存。改其中一个,另一个必然跟着变。
用 array 复制一份再改:A 还是 [1 2 3],C 是 [99 2 3]。
! 要独立就得 array 复制。asarray 省的是内存,代价是「共享」。
③ 什么时候用哪个
--------------------------------------------------------------------
| 情况 | 用哪个 | 为什么 |
|---|---|---|
| 输入可能不是数组 | asarray | 是的话直接返回,省一次复制 |
| 要改且不能影响原数据 | array | 复制一份 |
| 只是读一下 | asarray | 更快 |
一个常见写法:A = np.asarray(可能是列表或数组的东西),把两种输入统一。
! 函数入口用 asarray,函数内部要改就先 copy。这条能防住一多半「莫名其妙被改了」。
④ 从别的结构来
--------------------------------------------------------------------
从元组:[1 2 3]
从生成器得用 fromiter:[0 1 4 9]
从字符串:[1 2 3]
frombuffer、fromfile 是读二进制用的,日常很少碰。
! fromiter 要显式给 dtype。不给的话它没法推,直接报错。
A is B 给 True 说明是同一个对象。注意 is 比的是「是不是同一个」,== 比的是「值相等」。
asarray 省内存、array 保平安。没想清楚要不要改,就先用 array。
30 秒实验
- 把 asarray 换成 array,重跑一遍,看 A is B 变成什么。
- 给 asarray 传一个列表,看 A is B 还是不是 True。
- 想一想:如果函数内部把参数改了,调用方会受影响吗?
常见误区
- 以为 asarray 一定复制:遇到数组就不复制,改起来连带原数组。
- 用 == 判断是不是同一个对象:值相等不代表同一个,要用 is。
- fromiter 不给 dtype:直接报错,它推不出来。
- 函数里直接改传入的数组:调用方的数据被悄悄改了。
自测三问(点开对答案)
1. asarray 和 array 最本质的区别? —— 输入已经是 ndarray 时,asarray 直接返回它自己(不复制),array 一定复制一份。 2. 为什么改 asarray 的结果会影响原数组? —— 它们是同一块内存的两个名字,改哪个都是改那块内存。 3. 函数里拿到一个数组参数,要改应该怎么做? —— 先 `A = np.array(A)` 或 `A.copy()`,再改副本,别动调用方的数据。数组有了。下一篇讲它里面装的是什么类型,还有那个比循环快 51 倍的运算。
- ☐ (本篇完,下一篇 6.6 数据类型与数组运算)