python数据类型内部结构解剖
一、前言
我们知道,python是一种动态语言,可以将任何类型的数据赋给任何变量,譬如:
# Python代码
x = 4
x = "four"
这里已经将 x
变量的内容由整型转变成了字符串,而同样的操作在 C 语言中将会导致(取决于编译器设置)编译错误或其他未知的后果。
这种灵活性是使 Python 和其他动态类型的语言更易用的原因之一。理解这一特性如何工作是学习用 Python 有效且高效地分析数据的重要因素。但是这种类型灵活性也指出了一个事实:
Python 变量不仅是它们的值,还包括了关于值的类型的一些额外信息。
二、整型
标准的 Python 实现是用 C 语言编写的。这意味着每一个 Python 对象都是一个伪 C 语言结构体,该结构体不仅包含其值,还有其他信息。例如,当我们在 Python 中定义一个整型,例如 x = 10000
时,x
并不是一个“原生”整型,而是一个指针,指向一个 C 语言的复合结构体,结构体里包含了一些值。查看 Python 3.4 的源代码,可以发现整型(长整型)的定义,如下所示(C 语言的宏经过扩展之后):
struct _longobject {
long ob_refcnt;
PyTypeObject *ob_type;
size_t ob_size;
long ob_digit[];
};
Python 3.4 中的一个整型实际上包括 4 个部分。
ob_refcnt
是一个引用计数,它帮助 Python 默默地处理内存的分配和回收。ob_type
将变量的类型编码。ob_size
指定接下来的数据成员的大小。ob_digit
包含我们希望 Python 变量表示的实际整型值。
这意味着与 C 语言这样的编译语言中的整型相比,在 Python 中存储一个整型会有一些开销,正如下图所示:
这里 PyObject_HEAD
是结构体中包含引用计数、类型编码和其他之前提到的内容的部分。
两者的差异在于,C 语言整型本质上是对应某个内存位置的标签,里面存储的字节会编码成整型。而 Python 的整型其实是一个指针,指向包含这个 Python 对象所有信息的某个内存位置,其中包括可以转换成整型的字节。由于 Python 的整型结构体里面还包含了大量额外的信息,所以 Python 可以自由、动态地编码。但是,Python 类型中的这些额外信息也会成为负担,在多个对象组合的结构体中尤其明显。
三、列表
设想如果使用一个包含很多 Python 对象的 Python 数据结构,会发生什么? Python 中的标准可变多元素容器是列表。可以用如下方式创建一个整型值列表:
In[1]: L = list(range(10))
L
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In[2]: type(L[0])
Out[2]: int
或者创建一个字符串列表:
In[3]: L2 = [str(c) for c in L]
L2
Out[3]: ['', '', '', '', '', '', '', '', '', '']
In[4]: type(L2[0])
Out[4]: str
因为 Python 的动态类型特性,甚至可以创建一个异构的列表:
In[5]: L3 = [True, "", 3.0, 4]
[type(item) for item in L3]
Out[5]: [bool, str, float, int]
但是想拥有这种灵活性也是要付出一定代价的:为了获得这些灵活的类型,列表中的每一项必须包含各自的类型信息、引用计数和其他信息;也就是说,每一项都是一个完整的 Python 对象。来看一个特殊的例子,如果列表中的所有变量都是同一类型的,那么很多信息都会显得多余——将数据存储在固定类型的数组中应该会更高效。动态类型的列表和固定类型的(NumPy 式)数组间的区别如下图所示。
在实现层面,数组基本上包含一个指向连续数据块的指针。另一方面,Python 列表包含一个指向指针块的指针,这其中的每一个指针对应一个完整的 Python 对象(如前面看到的 Python 整型)。另外,列表的优势是灵活,因为每个列表元素是一个包含数据和类型信息的完整结构体,而且列表可以用任意类型的数据填充。固定类型的 NumPy 式数组缺乏这种灵活性,但是能更有效地存储和操作数据。
四、固定类型数组
1. 使用array模块创建数组
Python 提供了几种将数据存储在有效的、固定类型的数据缓存中的选项。内置的数组(array
)模块(在 Python 3.3 之后可用)可以用于创建统一类型的密集数组:
In[6]: import array
L = list(range(10))
A = array.array('i', L)
A
Out[6]: array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
这里的 'i'
是一个数据类型码,表示数据为整型。
更实用的是 NumPy 包中的 ndarray
对象。Python 的数组对象提供了数组型数据的有效存储,而 NumPy 为该数据加上了高效的操作。稍后将介绍这些操作,这里先展示几种创建 NumPy 数组的方法。
从用 np
别名导入 NumPy 的标准做法开始:
In[7]: import numpy as np
2. 从python列表创建NumPy 数组
首先,可以用 np.array
从 Python 列表创建数组:
In[8]: # 整型数组:
np.array([1, 4, 2, 5, 3])
Out[8]: array([1, 4, 2, 5, 3])
请记住,不同于 Python 列表,NumPy 要求数组必须包含同一类型的数据。如果类型不匹配,NumPy 将会向上转换(如果可行)。这里整型被转换为浮点型:
In[9]: np.array([3.14, 4, 2, 3])
Out[9]: array([ 3.14, 4. , 2. , 3. ])
如果希望明确设置数组的数据类型,可以用 dtype
关键字:
In[10]: np.array([1, 2, 3, 4], dtype='float32')
Out[10]: array([ 1., 2., 3., 4.], dtype=float32)
最后,不同于 Python 列表,NumPy 数组可以被指定为多维的。以下是用列表的列表初始化多维数组的一种方法:
In[11]: # 嵌套列表构成的多维数组
np.array([range(i, i + 3) for i in [2, 4, 6]])
Out[11]: array([[2, 3, 4],
[4, 5, 6],
[6, 7, 8]])
内层的列表被当作二维数组的行。
3. 从头创建NumPy 数组
面对大型数组的时候,用 NumPy 内置的方法从头创建数组是一种更高效的方法。以下是几个示例:
In[12]: # 创建一个长度为10的数组,数组的值都是0
np.zeros(10, dtype=int) Out[12]: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) In[13]: # 创建一个3×5的浮点型数组,数组的值都是1
np.ones((3, 5), dtype=float) Out[13]: array([[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.]]) In[14]: # 创建一个3×5的浮点型数组,数组的值都是3.14
np.full((3, 5), 3.14) Out[14]: array([[ 3.14, 3.14, 3.14, 3.14, 3.14],
[ 3.14, 3.14, 3.14, 3.14, 3.14],
[ 3.14, 3.14, 3.14, 3.14, 3.14]]) In[15]: # 创建一个3×5的浮点型数组,数组的值是一个线性序列
# 从0开始,到20结束,步长为2
# (它和内置的range()函数类似)
np.arange(0, 20, 2) Out[15]: array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) In[16]: # 创建一个5个元素的数组,这5个数均匀地分配到0~1
np.linspace(0, 1, 5) Out[16]: array([ 0. , 0.25, 0.5 , 0.75, 1. ]) In[17]: # 创建一个3×3的、在0~1均匀分布的随机数组成的数组
np.random.random((3, 3)) Out[17]: array([[ 0.99844933, 0.52183819, 0.22421193],
[ 0.08007488, 0.45429293, 0.20941444],
[ 0.14360941, 0.96910973, 0.946117 ]]) In[18]: # 创建一个3×3的、均值为0、方差为1的
# 正态分布的随机数数组
np.random.normal(0, 1, (3, 3)) Out[18]: array([[ 1.51772646, 0.39614948, -0.10634696],
[ 0.25671348, 0.00732722, 0.37783601],
[ 0.68446945, 0.15926039, -0.70744073]]) In[19]: # 创建一个3×3的、[0, 10)区间的随机整型数组
np.random.randint(0, 10, (3, 3)) Out[19]: array([[2, 3, 4],
[5, 7, 8],
[0, 5, 0]]) In[20]: # 创建一个3×3的单位矩阵
np.eye(3) Out[20]: array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]]) In[21]: # 创建一个由3个整型数组成的未初始化的数组
# 数组的值是内存空间中的任意值
np.empty(3) Out[21]: array([ 1., 1., 1.])
五、NumPy标准数据类型
NumPy 数组包含同一类型的值,因此详细了解这些数据类型及其限制是非常重要的。因为 NumPy 是在 C 语言的基础上开发的,所以 C、Fortran 和其他类似语言的用户会比较熟悉这些数据类型。
数据类型 |
描述 |
bool_ |
布尔值(真、True 或假、False),用一个字节存储 |
int_ |
默认整型(类似于 C 语言中的 long,通常情况下是 int64 或 int32) |
intc |
同 C 语言的 int 相同(通常是 int32 或 int64) |
intp |
用作索引的整型(和 C 语言的 ssize_t 相同,通常情况下是 int32 或 int64) |
int8 |
字节(byte,范围从–128 到 127) |
int16 |
整型(范围从–32768 到 32767) |
int32 |
整型(范围从–2147483648 到 2147483647) |
int64 |
整型(范围从–9223372036854775808 到 9223372036854775807) |
uint8 |
无符号整型(范围从 0 到 255) |
uint16 |
无符号整型(范围从 0 到 65535) |
uint32 |
无符号整型(范围从 0 到 4294967295) |
uint64 |
无符号整型(范围从 0 到 18446744073709551615) |
float_ |
float64 的简化形式 |
float16 |
半精度浮点型:符号比特位,5 比特位指数(exponent),10 比特位尾数(mantissa) |
float32 |
单精度浮点型:符号比特位,8 比特位指数,23 比特位尾数 |
float64 |
双精度浮点型:符号比特位,11 比特位指数,52 比特位尾数 |
complex_ |
complex128 的简化形式 |
complex64 |
复数,由两个 32 位浮点数表示 |
complex128 |
复数,由两个 64 位浮点数表示 |
上表列出了标准 NumPy 数据类型。请注意,当构建一个数组时,你可以用一个字符串参数来指定数据类型:
np.zeros(10, dtype='int16')
或者用相关的 NumPy 对象来指定:
np.zeros(10, dtype=np.int16)
还可以进行更高级的数据类型指定,例如指定高位字节数或低位字节数;更多的信息可以在 NumPy 文档(http://numpy.org/)中查看。
六、参考
1. 《Python数据科学手册》 [美] Jake VanderPlas [VanderPlas, Jake]
(完)
python数据类型内部结构解剖的更多相关文章
- python 数据类型---布尔型& 字符串
python数据类型-----布尔型 真或假=>1或0 >>> 1==True True >>> 0==False True python 数据类型----- ...
- Python 数据类型及其用法
本文总结一下Python中用到的各种数据类型,以及如何使用可以使得我们的代码变得简洁. 基本结构 我们首先要看的是几乎任何语言都具有的数据类型,包括字符串.整型.浮点型以及布尔类型.这些基本数据类型组 ...
- day01-day04总结- Python 数据类型及其用法
Python 数据类型及其用法: 本文总结一下Python中用到的各种数据类型,以及如何使用可以使得我们的代码变得简洁. 基本结构 我们首先要看的是几乎任何语言都具有的数据类型,包括字符串.整型.浮点 ...
- Python数据类型及其方法详解
Python数据类型及其方法详解 我们在学习编程语言的时候,都会遇到数据类型,这种看着很基础也不显眼的东西,却是很重要,本文介绍了python的数据类型,并就每种数据类型的方法作出了详细的描述,可供知 ...
- Python学习笔记(五)--Python数据类型-数字及字符串
Python数据类型:123和'123'一样吗?>>> 123=='123'False>>> type(123)<type 'int'>>> ...
- python数据类型之元组、字典、集合
python数据类型元组.字典.集合 元组 python的元组与列表类似,不同的是元组是不可变的数据类型.元组使用小括号,列表使用方括号.当元组里只有一个元素是必须要加逗号: >>> ...
- 1 Python数据类型--
常见的Python数据类型: (1)数值类型:就是平时处理的数字(整数.浮点数) (2)序列类型:有一系列的对象并排或者排列的情况.如字符串(str),列表(list),元组(tuple)等 (3)集 ...
- Python数据类型和数据操作
python数据类型有:int,float,string,boolean类型.其中string类型是不可变变量,用string定义的变量称为不可变变量,该变量的值不能修改. 下面介绍python中的l ...
- Python数据类型(python3)
Python数据类型(python3) 基础数据类型 整型 <class 'int'> 带符号的,根据机器字长32位和64位表示的范围不相同,分别是: -2^31 - 2^31-1 和 - ...
随机推荐
- Vue引用其他组件,但组件某些部分不需要时的简单处理
项目开发时,我们会把多个地方重复使用的模块抽象成组件,提供给大家一起使用,但是使用组件的时候偶尔会遇见一些问题,比如说组件里只有某些东西自己并不需要,这个时候我们可以对组件进行简单的修改,而不影响其他 ...
- Delphi7到Delphi XE2的升级历程
1.PChar 转为PAnsiChar; 2.第三方控件的安装 SuiPack不能直接点击InStall.exe安装,需要打开DPK文件安装: SuiPack安装之后程序编译会报错,resHandle ...
- Docker集群管理(一)—— 基础docker+swarm+shipyard
目的 学习docker的集群管理,摸索出高可用的docker微服务架构方案.本篇文章只初步的了解下swarm(docker新版已集成了swarm)的使用,了解docker的发现服务的基础方法(dock ...
- sqoop产生背景及概述
sqoop产生背景 多数是用Hadoop技术处理大数据业务的企业有大量的数据存储在传统的关系型数据库(RDBMS)中:由于缺乏工具的支持.对Hadoop和传统数据库系统中的数据进行相互传输是一件十分困 ...
- 阳虚体质外感/胃脘痛/经期抽搐x案
* 咽干咽痛 某女 42岁 在40岁产下一子,后体质明显不如以前,几年以来,易感冒,咳嗽 每次在社区医院输液,少则一个月,多则几个月方能愈,几天前外感微咳,咽痛声嘶 观其咽并不红,舌淡苔薄白 双手 ...
- logger常用方法
#!/usr/bin/env python # encoding: utf-8 # Date: 2018/5/25 import loggingfrom logging import handler ...
- 2018-2019-2 《网络对抗技术》Exp5 MSF基础应用 Week7-8 20165233
Exp5 MSF基础应用 目录 一.基础问题 二.攻击实例 主动攻击 ms08_067_netapi(成功) ms10_061_spoolss(失败) 针对浏览器的攻击 ms14_064_ole_co ...
- C++官方文档-运算符重载
#include <iostream> using namespace std; class CVector { public: int x, y; CVector() : x(), y( ...
- 解决Specifying a namespace in include()withou providing an app_name
python3 Django 环境下,如果你遇到namespace没有注册以及在根目录下urls.py中的include方法的第二个参数namespace添加之后就出错的问题.请在[app_name] ...
- python入门-类(二)
1 关于类的导入 可以把类封装到1个文件中 1个文件中也可以封装多个类 在导入的时候可以导入单个,也可以导入多个类,也可以全部导入类 car.py """一个可以用于表示 ...