Python文件处理2

版权声明:原创文章转载请注明出处~ https://blog.csdn.net/PecoHe/article/details/90174362

14.3 序列化

在我们开发过程中,我们会读取或设置配置信息,对数据进行分析,将爬取的数据保存等……因此,我们可能会操作不同类型的文件。本节会介绍csv与json文件类型的特征,然后给出该类型文件的读取与写入方式。

14.3.1 csv

CSV(Comma Separated Values),是一种存文本格式的文件,文件中各个元素值通常使用逗号(,)进行分隔,但这不是必须的,扩展名为.csv。例如,一个班级的学生信息,我们就可以存储为csv的格式:

姓名,年龄,所在小组
张某,15,A组
赵某,14,B组
王某,15,C组
……

我们可以使用csv模块来操作csv类型的文件

14.3.2 json

JSON(JavaScript Object Notation),是一种轻量级的数据交换格式。json采用的是一组键与值的映射,键与值之间使用“:”进行分隔,而键值对之间使用“,”进行分隔。json文件中的类型可以是:

  • 对象类型:使用{}表示。
  • 数组类型:使用[]表示。
  • 字符串类型:使用双引号界定。
  • 布尔类型:true与false。
  • 数值类型:整数与浮点数。例如5,2.8等。

json格式的文件示例如下:

{
    "bg": "green",
    "title": {
        "data": ["data1", "data2", "data3", "data4"],
        "align": "左对齐"
    }
}

从json文件的文件格式我们发现,jaon非常类似于Python中的字典类型。的确,我们可以在json文件与Python中的字典类型之间进行转换。json模块提供了相关的功能。
处理程序【dump&load】

使用json格式的文件可以方便的进行数据交换。我们可以通过json类型的数据进行对象的序列化与反序列化。所谓序列化,就是将对象类型转换成字符串的形式。而反序列化,即为将序列化的字符串恢复为对象类型。通过序列化与反序列化,我们就可以方便的对复杂的对象进行存储与恢复(因为文件读写只支持字符串类型),或者通过网络进行传输,将对象共享给远程的其他程序使用。

序列化与反序列化程序【dumps & loads】

因为Python中的数据类型与json格式的数据类型并非完全相符,因此,在进行转换的时候,可能会进行一些映射处理,如下(json -> Python):

  • 布尔类型(true与false)映射Python中布尔类型(True与False)。
  • 空值类型(null)映射为None。
  • 整数与浮点类型映射为整数(int)与(float)类型。
  • 字符串类型映射为字符串(str)类型。
  • 数组类型([])映射为列表(list)类型。
  • 对象类型(object)映射为字典(dict)类型。

映射程序示例。

json在序列化时,不能序列化我们自定义的类型(以上类型之外的类型)。如果我们需要自定义的类型也能够序列化,可以定义一个编码类,该类继承json.JSONEncoder,实现类中的default方法,指定序列化的方式,同时,在调用序列化方法时(dump或dumps),使用cls参数指定我们定义的编码类。
自定义序列化程序

14.3.3 pickle

我们也可以使用pickle模块提供的功能来序列化类型。在序列化自定义类型上,pickle可以比json模块更加方便(不需要定义类似的编码器类)。
pickle与json在序列化上的区别见下表。
表格 15 2 json与pickle在序列化上的区别
序列化 json pickle
序列化格式:文本格式,可进行正常查看。 二进制格式,不方便查看。
序列化类型的支持:支持一部分内建的类型,如果需要序列化自定义类型,需要编写编码类。 支持非常广泛的类型,包括自定义类型,不需要编写编码类。
适用广泛性:适用广泛,对于序列化的内容可以用于Python语言之外的程序中进行反序列化。 适用受限,只能用于Python程序中,其他语言的程序无法反序列化。
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。

14.4 上下文管理器

14.4.1 自定义上下文管理器

我们之前介绍过with的使用。通过with,我们可以代替try-finally语句,实现更简洁的资源清理工作。然而,with为什么有这种神奇的特性呢?是什么给了with如此的保证?

实际上,with语句跟随的表达式会返回一个上下文管理器,该上下文管理器中定义相关方法,在with开始执行与退出时会调用,也就是说,上下文管理器为with提供一个执行环境

	__enter__(self)

with语句体开始执行时,会调用该方法。我们可以在该方法中执行某些初始化的操作,该方法的返回值会赋值给with语句中as后面的变量。

__exit__(self, exc_type, exc_val, exc_tb)

with语句体执行结束后,会调用该方法。我们在__enter__方法中执行的初始化,就可以在该方法中执行相关的清理,例如,文件的关闭,线程锁的释放,状态的恢复等。这就可以实现于finally语句同样的功能。相关参数如下:

  • exc_type:产生异常的类型。
  • exc_val:产生异常类型的对象。
  • exc_tb:traceback类型的对象,包含了异常产生位置的堆栈调用信息。

如果在with语句体中没有产生异常,则相关的参数(exc_type,exc_val与 exc_tb)的值为None。否则,异常类,异常对象与轨迹信息会依次传递给这三个参数,此时,如果该方法的返回值为True,则压制异常,即异常不会在with语句体结束后继续抛出,如果方法的返回值为False,则异常继续抛出。
对于with,也可以关联两个上下文管理器,例如:

with Manager1() as m1, Manager2 as m2:
    语句
这相当于是:
with Manager1() as m1:
    with Manager2() as m2:
        语句

14.4.2 @contextmanager装饰器

在contextlib模块中,定义了@contextmanager装饰器,该装饰器可以用来修饰一个生成器,从而将生成器变成一个上下文管理器,从而可以省略编写完整的上下文管理器类,在一定程度上可以简化程序。
关于@contextmanager装饰器,有以下几点说明:

  • 在@contextmanager修饰的生成器中,yield之前的语句会在进入with语句体时执行(相当于__enter__方法),而yield之后的语句会在离开with语句体时执行(相当于__eixt__方法)。
  • with后的表达式会返回生成器对象(假设为gen_obj),进入with语句体时,内部会调用next函数,用来激活生成器对象,进而执行生成器的函数体:next(gen_obj)
  • 从而令生成器对象执行。yield产生的值则会赋值给with语句as后的变量(相当于__enter__方法的返回值)。
  • 当with语句体结束时,如果with语句体没有产生异常,则继续调用next,令生成器从之前yield暂停的位置处继续执行(这相当于实现__exit__方法)。如果with语句体产生异常,该异常会在生成器函数体yield的位置抛出。而如果生成器函数体没有处理该异常,将会导致yield之后的语句不会得到执行,这相当于是没有成功的执行__exit__方法。因此,为了能够保证yield之后的语句能够得到执行,我们应该总是在生成器的函数体内使用try-finally语句。

猜你喜欢

转载自blog.csdn.net/PecoHe/article/details/90174362
今日推荐