Python 使用对象模型来存储数据?

Python017

Python 使用对象模型来存储数据?,第1张

Python使用对象模型来存储数据。构造任何类型的值都是一个对象。尽管Python通常被当成一种“面向对象的编程语言”,但你完全能够写出不使用任何类和实例的实用脚本。不过Python的对象语法和架构鼓励我们使用这些特性,下面让我们仔细研究一下Python对象。所有的Python对像都拥有三个特性:身份,类型和值。

身份:每一个对象都有一个唯一的身份标识自己,任何对象的身份可以使用内建函数id()来得到。这个值可以被认为是该对象的内存地址。你极少会用到这个值,也不用太关心它究竟是什么。

类型对象的类型决定了该对象可以保存什么类型的值,可以进行什么样的操作,以及遵循什么样的规则。你可以用内建函数type0查看Python对象的类型。因为在Python中类型也是对象(还记得我们提到Python是面向对象的这句话吗?),所以type0返回的是对象而不是简单的字符串。

值:对象表示的数据项。

上面三个特性在对象创建的时候就被赋值,除了值之外,其他两个特性都是只读的。对于新式类型和类,对象的类型也是可以改变的,不过并不推荐初学者这样做。如果对象支持更新操作,那么它的值就可以改变,否则它的值也是只读的。对象的值是否可以更改被称为对象的可改变性(mutability),我们会在后面的4.7小节中讨论这个问题。只要一个对象还没有被销毁,这些特性就一直存在。Python有一系列的基本(内建)数据类型,必要时也可以创建自定义类型来满足你对应用程序的需求。绝大多数应用程序通常使用标准类型,对特定的数据存储则通过创建和实例化类来实现。

众所周知,Python是一门面向对象的语言,在Python无论是数值、字符串、函数亦或是类型、类,都是对象。

对象是在 堆 上分配的结构,我们定义的所有变量、函数等,都存储于堆内存,而变量名、函数名则是一个存储于 栈 中、指向堆中具体结构的引用。

要想深入学习Python,首先需要知道Python对象的定义。

我们通常说的Python都是指CPython,底层由C语言实现,源码地址: cpython [GitHub]

Python对象的定义位于 Include/object.h ,是一个名为 PyObject 的结构体:

Python中的所有对象都继承自PyObejct,PyObject包含一个用于垃圾回收的双向链表,一个引用计数变量 ob_refcnt 和 一个类型对象指针 ob_type

从PyObejct的注释中,我们可以看到这样一句:每个指向 可变大小Python对象 的指针也可以转换为 PyVarObject* (可变大小的Python对象会在下文中解释)。 PyVarObejct 就是在PyObject的基础上多了一个 ob_size 字段,用于存储元素个数:

在PyObject结构中,还有一个类型对象指针 ob_type ,用于表示Python对象是什么类型,定义Python对象类型的是一个 PyTypeObject 接口体

实际定义是位于 Include/cpython/object.h 的 _typeobject :

在这个类型对象中,不仅包含了对象的类型,还包含了如分配内存大小、对象标准操作等信息,主要分为:

以Python中的 int类型 为例,int类型对象的定义如下:

从PyObject的定义中我们知道,每个对象的 ob_type 都要指向一个具体的类型对象,比如一个数值型对象 100 ,它的ob_type会指向 int类型对象PyLong_Type 。

PyTypeObject结构体第一行是一个PyObject_VAR_HEAD宏,查看宏定义可知PyTypeObject是一个变长对象

也就是说,归根结底 类型对象也是一个对象 ,也有ob_type属性,那 PyLong_Type 的 ob_type 是什么呢?

回到PyLong_Type的定义,第一行 PyVarObject_HEAD_INIT(&PyType_Type, 0) ,查看对应的宏定义

由以上关系可以知道, PyVarObject_HEAD_INIT(&PyType_Type, 0) = { { _PyObject_EXTRA_INIT 1, &PyType_Type } 0} ,将其代入 PyObject_VAR_HEAD ,得到一个变长对象:

这样看就很明确了,PyLong_Type的类型就是PyType_Typ,同理可知, Python类型对象的类型就是PyType_Type ,而 PyType_Type对象的类型是它本身

从上述内容中,我们知道了对象和对象类型的定义,那么根据定义,对象可以有以下两种分类

Python对象定义有 PyObject 和 PyVarObject ,因此,根据对象大小是否可变的区别,Python对象可以划分为 可变对象(变长对象) 和 不可变对象(定长对象)

原本的对象a大小并没有改变,只是s引用的对象改变了。这里的对象a、对象b就是定长对象

可以看到,变量l仍然指向对象a,只是对象a的内容发生了改变,数据量变大了。这里的对象a就是变长对象

由于存在以上特性,所以使用这两种对象还会带来一种区别:

声明 s2 = s ,修改s的值: s = 'new string' ,s2的值不会一起改变,因为只是s指向了一个新的对象,s2指向的旧对象的值并没有发生改变

声明 l2 = l ,修改l的值: l.append(6) ,此时l2的值会一起改变,因为l和l2指向的是同一个对象,而该对象的内容被l修改了

此外,对于 字符串 对象,Python还有一套内存复用机制,如果两个字符串变量值相同,那它们将共用同一个对象:

对于 数值型 对象,Python会默认创建0~2 8 以内的整数对象,也就是 0 ~ 256 之间的数值对象是共用的:

按照Python数据类型,对象可分为以下几类:

Python创建对象有两种方式,泛型API和和类型相关的API

这类API通常以 PyObject_xxx 的形式命名,可以应用在任意Python对象上,如:

使用 PyObjecg_New 创建一个数值型对象:

这类API通常只能作用于一种类型的对象上,如:

使用 PyLong_FromLong 创建一个数值型对象:

在我们使用Python声明变量的时候,并不需要为变量指派类型,在给变量赋值的时候,可以赋值任意类型数据,如:

从Python对象的定义我们已经可以知晓造成这个特点的原因了,Python创建对象时,会分配内存进行初始化,然后Python内部通过 PyObject* 变量来维护这个对象,所以在Python内部各函数直接传递的都是一种泛型指针 PyObject* ,这个指针所指向的对象类型是不固定的,只能通过所指对象的 ob_type 属性动态进行判断,而Python正是通过 ob_type 实现了多态机制

Python在管理维护对象时,通过引用计数来判断内存中的对象是否需要被销毁,Python中所有事物都是对象,所有对象都有引用计数 ob_refcnt 。

当一个对象的引用计数减少到0之后,Python将会释放该对象所占用的内存和系统资源。

但这并不意味着最终一定会释放内存空间,因为频繁申请释放内存会大大降低Python的执行效率,因此Python中采用了内存对象池的技术,是的对象释放的空间会还给内存池,而不是直接释放,后续需要申请空间时,优先从内存对象池中获取。

Pandas数据结构详细说明及如何创建Series,DataFrame对象方法

2021-10-06 13:45:19 投稿:wdc

本篇文章中,我们主要侧重于介绍Pandas数据结构本身的特性,以及如何创建一个Series或者DataFrame数据对象,并填入一些数据

在网络上的Pandas教程中,很多都提到了如何使用Pandas将已有的数据(如csv,如hdfs等)直接加载成Pandas数据对象,然后在其基础上进行数据分析操作,但是,很多时候,我们需要自己创建Pandas数据对象,并填入一些数据,常见的应用场景如:我们想要将现有的数据进行处理,并生成一个新的Pandas数据对象,还有,我们想利用Pandas的数据保存功能(比如to_csv, to_json, to_hdf等等)把我们采集到的数据写入到IO里边,因此掌握Pandas对象的特性,以及如何创建也是很重要的。

有些时候我们需要利用pandas数据结构创建自己的对象,按自己的方式保存新数据,我们将在本文中介绍如何实现。

1. Pandas的两种数据类型

Pandas支持两种数据类型,分别为Series和DataFrame,其中:

Series - 是一个带有标签的一维数组,支持多种不同的类型,但是针对同一个Series里边存储的数据类型必须是一致的

DataFrame - 是一个带有标签的二维数组,是一个尺寸可以修改的表格,一个DataFrame由多个Series组成,每一列都是一个Series

一句话描述的话就是,Series是很多标量数据(Scalar)的集合,而DataFrame是很多Series的集合。

我们来看下图这个例子,在1D的Series中,下图中有三个Series,分别保存了姓名(name), 年龄(age)和得分(marks),而他们的每一行都分别对应一个不同的人的信息,在每一个Series中的每一个单元格中(比如name series的第1行,对应的Prasadi)都是一个标量(Scalar),而每一行前边的0,1,2,3这些就是数据的索引(index),也可以叫做标签,所以说,Series是带有标签的一维数组。

可以看出,利用Series只能存储一种类型的数据,比如说name series存储的数据是字符串类型,而age series存储的数据是整数型。如果我们想把name,age,marks存储在一个数据结构里,我们就需要使用DataFrame,从图中看出,DataFrame类似于一个表格数据,有行有列,行跟Series的行一致,是数据的标签,而每一列就是原来的每一个Series。

2. Series类型

如我们在前文中所说,Series结构中可以存储任何类型的数据,包括:整型,字符串类型,浮点型,甚至是Python对象等等,但是要求是,每一行的数据类型必须统一。那么如何创建一个Series对象呢,

通过numpy array

Pandas的一个主要用途是数据分析,而它也是基于Numpy实现的,因此,通过numpy array来创建Series是非常常见的。

np_array = np.random.randn(5)

pd.Series(np_array, index=['a', 'b', 'c', 'd', 'e'])

上边这段代码,利用np.random.randn随机生成一个长度为5的numpy array,然后pd.Series使用这个numpy array来创建一个Series,在创建的同时,指定了每一行的index(标签)分别是a,b,c,d,e,f,输出结果为:

通过Python字典

通过上边这个示例,大家有没有发现Series跟Python内置的dict类型是不是很类似,标签相当于dict中的key,而数据内容相当于dict中的value,它们有一一对应的关系,因此,可以想象,我们能够直接通过Python的dict来创建一个Series。

d = {'b': 1, 'a': 0, 'c': 2}

pd.Series(d)

上边这段代码,我们先创建了一个Python地点d,然后将这个字典传递给pd.Series来创建一个Pandas Series,运行结果为:

通过标量值(Scalar)

除了上边这两种方式,我们还可以通过一个简单的标量值来创建Series,特别注意的是跟上边两种方式不同,在使用这种方式创建Series的时候,我们必须指定index

pd.Series(5, index=['a', 'b', 'c', 'd', 'e'])

如上边代码所示,我们使用一个常量5,然后指定index为a,b,c,d,e,同样使用pd.Series可以创建一个Series对象,看到这里我们就能够明白为什么必须指定Index了吧,那是因为Series对象是有长度的,长度是可以大于1的,而标量的长度固定为1,我们可以通过指定Index的方式来控制生成的Series的长度,Series中的值则是重复使用这一个标量常量5。其运行结果为:

name属性

当我们创建一个Series的时候,我们可以指定一个名称,这个名称会被存储到Series的name属性中,后续我们还可以使用rename方法来修改这个属性,例如下边这样的代码:

s = pd.Series(np.random.randn(5), name='this_is_name')

s

创建了一个名称为this_is_name的Series,然后我们使用rename方法来重命名这个Series为this_is_new_name:

s = s.rename('this_is_new_name')

s

上边这两部分代码的输入如下图:

那么这个名称有什么作用呢,这里预告一下,我们将在DataFrame中用到(别忘了DataFrame是多个Series的集合)

3. DataFrame类型

在第一节中我们介绍到,DataFrame是一个二维的表格数据结构,它有行和列的概念,跟行标签相对应的,为了能够按列索引数据,每一列都可以有一个名称,即列名,我们刚在Series章节中看到,Series可以表示一列数据,我们在本节中介绍的DataFrame就是多个这样的Series的组合,每一列就对应一个Series,而每一行也对应一个Series。读到这里,你是不是能够猜的出我们刚说的Series的name属性的用途了,对了,使用Series创建DataFrame的列的时候,Series的名称就会成为列名,如果Series作为行,则Series的名称会成为行名。

接下来我们来讲解如何创建DataFrame

通过一维numpy array或者Python List 组成的字典

大家可以想想,如果一个字典的value是array或者list的时候,那么这个字典其实就是一种表格结构,图为DataFrame是一个表格结构的数据类型,我们是可以通过这样的字典来创建DataFrame,例如下边这段代码

d = {'one': [1., 2., 3., 4.],

'two': [4., 3., 2., 1.]}

pd.DataFrame(d, index=['a', 'b', 'c', 'd'])

我们把d这个Python字典传递给pd.DataFrame来创建新的DataFrame,同时我们可以通过指定index来指定DataFrame的行名(标签),上边代码的输出为

通过包含列表的Python List

我们再来想想一下,除了字典之外可以表示表格数据,还有没有其他的方法,是的,还有Python List,例如下边这段代码

data = [(1, 2., 'Hello'), (2, 3., "World")]

pd.DataFrame(data)

我们可以用data这样的Python List来表示表格数据,不同于前边提到的字典(dict),用List表示的表格数据其实是没有行名和列名的,因此Pandas默认会自动生成行名和列名,所以上边的代码输出为:

当然,自动生成的行名列名没有任何意义,为了更好的操作数据,我们还可以通过设置pd.DataFrame方法的index或者columns参数来指定自己的行名或者列名。

通过包含Python 字典的Python List

我们继续想想,还有什么能够表示表格数据?对了,包含Python字典的Python List也是可以表达表格数据的,例如下边的代码

data = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]

pd.DataFrame(data)

data是一个Python List,而列表中的每一个元素都是一个字典,运行结果为:

类似的,我们也可以通过指定index或者columns参数来修改行名和列名

通过Series

我们一直在提DataFrame是很多Series的集合(注:Series在DataFrame中可以是一行,也可以是一列),因此,我们也可以通过Series来创建DataFrame,例如下边这段代码

s1 = pd.Series(np.random.randn(5), name='this_is_name')

df = pd.DataFrame(s1)

df

利用s1这个Series来创建只有一列的DataFrame,输出结果为:

还记得不,我们前边提到了Series的name属性,在使用Series创建DataFrame的时候,这个属性会用来作为列名(或者行名,我们在下边的列子可以看得出),例如下边的这段代码,如果有两个Series,我们还可以用下边这样的方式创建DataFrame

s1 = pd.Series(np.random.randn(5), name='this_is_name')

s2 = s.rename('this_is_new_name')

df = pd.DataFrame([s1, s2])

df

这里我们使用了两个名分别为this_is_name和this_is_new_name的Series来创建DataFrame,得到的结果为:

到这里,相信读者已经对Pandas提供的数据类型有了一个全面的认识了,并且有能力自己创建Pandas数据结构,并存储自己的数据了,一个常见的应用场景就是我们通过爬虫获取到数据以后,可以将这些非结构化的数据以Pandas的表格格式保存,值得注意的是数据存储在Pandas数据结构中的时候,数据其实是在内存中的,当程序被关闭以后,数据就丢失了,如果我们需要将数据持久化保存到硬盘或者数据库中的话,则可以通过简单的调用Pandas提供的to_csv, to_json, to_hdf等等接口将数据永久保存下来。

更多Python Pandas库的相关文章,请点击下面的相关文章

您可能感兴趣的文章:

Python数据分析 Pandas Series对象操作

使用python计算方差方式——pandas.series.std()

教你漂亮打印Pandas DataFrames和Series

pandas的Series类型与基本操作详解

Pandas把dataframe或series转换成list的方法

pandas中的series数据类型详解

浅谈Pandas Series 和 Numpy array中的相同点

Pandas中Series的属性,方法,常用操作使用案例

相关文章

Python浅析迭代器Iterator的使用

Python浅析迭代器Iterator的使用

2022-07-07

python面试题之read、readline和readlines的区别详解

python面试题之read、readline和readlines的区别详解

2022-07-07

Python利用LyScript插件实现批量打开关闭进程

Python利用LyScript插件实现批量打开关闭进程

2022-07-07

Pandas 筛选和删除目标值所在的行的实现

Pandas 筛选和删除目标值所在的行的实现

2022-07-07

最近更新

Python动态配置管理Dynaconf的实现示例详解

Python动态配置管理Dynaconf的实现示例详解

2022-07-07

Python中np.linalg.norm()用法实例总结

Python中np.linalg.norm()用法实例总结

2022-07-07

python生成requirements.txt文件的推荐方法

python生成requirements.txt文件的推荐方法

2022-07-07

Python3读取文件的操作详解

Python3读取文件的操作详解

2022-07-07

业界资讯

美国设下计谋,用娘炮文化重塑日本,已影响至中国

美国设下计谋,用娘炮文化重塑日本,已影响至中国

2021-11-19

时空伴随者是什么意思?时空伴随者介绍

时空伴随者是什么意思?时空伴随者介绍

2021-11-09

工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终

工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终

2021-11-05

2022年放假安排出炉:五一连休5天 2022年所有节日一览表

2022年放假安排出炉:五一连休5天 2022年所有节日一览表

2021-10-26

电脑版 - 返回首页

2006-2022 脚本之家 JB51.Net , All Rights Reserved.

苏ICP备14036222号