Python基础入门自学——7

IO编程

IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

以访问新浪首页为例,浏览器这个程序就需要通过网络IO获取新浪的网页。浏览器首先会发送数据给新浪服务器,告诉它我想要首页的HTML,对于浏览器这个动作是往外发数据,叫Output,对于新浪服务器,这是接收数据,叫做Input,随后新浪服务器把网页发过来,对于浏览器这个动作是从外面接收数据,叫Input,对于新浪服务器,这是发送数据,叫Output。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。

IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和新浪服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题。举例,比如要把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可是磁盘要接收这100M数据可能需要10秒,怎么办呢?有两种办法:

第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO

另一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO

同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。

你说“来个汉堡”,服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场),这是异步IO。

很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。想想看,你得知道什么时候通知你“汉堡做好了”,而通知你的方法也各不相同。如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步IO的复杂度远远高于同步IO。

操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也不例外。

先学习同步模式IO编程。

文件读写
读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。

读写文件前,必须了解在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

读文件

不管是读文件还是写文件,前提都是要先打开文件,打开文件使用open()内置函数。
要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符:

读文件,open函数参数第一个是读文件的具体位置,或叫做路径,第二个参数是'r',表示以只读的方式打开文件。第一次运行提示FileNotFoundError,是因为在hellop.txt文件不存在,然后在当前工作目录中创建一个hellop.txt文件后,再次运行,正常。

如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:

因为是空文件,所以没有内容输出,修改hellop.txt文件:

出现了解码错误,不能使用‘gbk’来解码文件内容。关于open()函数:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
打开 file 并返回对应的 file object。 如果该文件不能被打开,则引发 OSError。

file 是一个 path-like object,表示将要打开的文件的路径(绝对路径或者当前工作目录的相对路径),也可以是要被封装的整数类型文件描述符。(如果是文件描述符,它会随着返回的 I/O 对象关闭而关闭,除非 closefd 被设为 False 。)

mode 是一个可选字符串,用于指定打开文件的模式。默认值是 'r' ,这意味着它以文本模式打开并读取。其他常见模式有:写入 'w' (截断已经存在的文件,即清空文件)排它性创建 'x' ;追加写 'a' (在 一些 Unix 系统上,无论当前的文件指针在什么位置,所有 写入都会追加到文件末尾)。在文本模式,如果 encoding 没有指定,则根据平台来决定使用的编码:使用 locale.getpreferredencoding(False) 来获取本地编码。(要读取和写入原始字节,请使用二进制模式并不要指定 encoding。)可用的模式有:

字符

含义

'r'

读取(默认)

'w'

写入,并先截断文件

'x'

排它性创建,如果文件已存在则失败

'a'

写入,如果文件存在则在末尾追加

'b'

二进制模式

't'

文本模式(默认)

'+'

打开用于更新(读取与写入)

默认模式为 'r' (打开用于读取文本,与 'rt' 同义)。 模式 'w+' 与 'w+b' 将打开文件并清空内容。 模式 'r+' 与 'r+b' 将打开文件并不清空内容。

Python区分二进制和文本I/O。以二进制模式打开的文件(包括 mode 参数中的 'b' )返回的内容为 bytes`对象,不进行任何解码。在文本模式下(默认情况下,或者在 *mode* 参数中包含 `'t'` )时,文件内容返回为 str ,首先使用指定的 encoding (如果给定)或者使用平台默认的的字节编码解码

cp936,就是编码就是gbk。

再看要打开的文件hellop.txt:

它使用的是UTF-8编码,那么打开时使用gbk就肯定不行的。可以更改open打开时使用的编码方案,即encoding改为utf8:

还可以更改hellop.txt文件的编码,

也可以正确的读出来。open函数的encoding是用来指定对读入的文件进行解码的,一般都要与要读的文件的编码一致。

Python不依赖于底层操作系统的文本文件概念;所有处理都由Python本身完成,因此与平台无关。

buffering 是一个可选的整数,用于设置缓冲策略。传递0以切换缓冲关闭(仅允许在二进制模式下),1选择行缓冲(仅在文本模式下可用),并且>1的整数以指示固定大小的块缓冲区的大小(以字节为单位)。如果没有给出 buffering 参数,则默认缓冲策略的工作方式如下:

● 二进制文件以固定大小的块进行缓冲;使用启发式方法选择缓冲区的大小,尝试确定底层设备的“块大小”或使用 io.DEFAULT_BUFFER_SIZE。在许多系统上,缓冲区的长度通常为4096或8192字节。

● “交互式”文本文件( isatty() 返回 True 的文件)使用行缓冲。其他文本文件使用上述策略用于二进制文件。

encoding 是用于解码或编码文件的编码的名称。这应该只在文本模式下使用默认编码是依赖于平台的(不 管 locale.getpreferredencoding() 返回何值),但可以使用任何Python支持的 text encoding 。

errors 是一个可选的字符串参数,用于指定如何处理编码和解码错误 - 这不能在二进制模式下使用。可以使用各种标准错误处理程序(列在 错误处理方案 ),但是使用 codecs.register_error() 注册的任何错误处理名称也是有效的。标准名称包括:

    ● 如果存在编码错误,'strict' 会引发 ValueError 异常。 默认值 None 具有相同的效果。
    ● 'ignore' 忽略错误。请注意,忽略编码错误可能会导致数据丢失。
    ● 'replace' 会将替换标记(例如 '?' )插入有错误数据的地方。
    ● 'surrogateescape' 将表示任何不正确的字节作为Unicode专用区中的代码点,范围从U+DC80到U+DCFF。当在写入数据时使用 surrogateescape 错误处理程序时,这些私有代码点将被转回到相同的字节中。这对于处理未知编码的文件很有用。
    ● 只有在写入文件时才支持 'xmlcharrefreplace'。编码不支持的字符将替换为相应的XML字符引用 &#nnn;。
    ● 'backslashreplace' 用Python的反向转义序列替换格式错误的数据。
    ● 'namereplace' (也只在编写时支持)用 \N{...} 转义序列替换不支持的字符。

newline 控制 universal newlines 模式如何生效(它仅适用于文本模式)。它可以是 None,'','\n','\r' 和 '\r\n'。它的工作原理:

    ● 从流中读取输入时,如果 newline 为 None,则启用通用换行模式。输入中的行可以以 '\n','\r' 或 '\r\n' 结尾,这些行被翻译成 '\n' 在返回呼叫者之前。如果它是 '',则启用通用换行模式,但行结尾将返回给调用者未翻译。如果它具有任何其他合法值,则输入行仅由给定字符串终止,并且行结尾将返回给未调用的调用者。
    ● 将输出写入流时,如果 newline 为 None,则写入的任何 '\n' 字符都将转换为系统默认行分隔符 os.linesep。如果 newline 是 '' 或 '\n',则不进行翻译。如果 newline 是任何其他合法值,则写入的任何 '\n' 字符将被转换为给定的字符串。

如果 closefd 是 False 并且给出了文件描述符而不是文件名,那么当文件关闭时,底层文件描述符将保持打开状态。如果给出文件名则 closefd 必须为 True (默认值),否则将引发错误。

可以通过传递可调用的 opener 来使用自定义开启器。然后通过使用参数( file,flags )调用 opener 获得文件对象的基础文件描述符。 opener 必须返回一个打开的文件描述符(使用 os.open as opener 时与传递 None 的效果相同)。

新创建的文件是 不可继承的。

open() 函数所返回的 file object 类型取决于所用模式。 当使用 open() 以文本模式 ('w', 'r', 'wt', 'rt' 等) 打开文件时,它将返回 io.TextIOBase (特别是 io.TextIOWrapper) 的一个子类。 当使用缓冲以二进制模式打开文件时,返回的类是 io.BufferedIOBase 的一个子类。 具体的类会有多种:在只读的二进制模式下,它将返回 io.BufferedReader;在写入二进制和追加二进制模式下,它将返回 io.BufferedWriter,而在读/写模式下,它将返回 io.BufferedRandom。 当禁用缓冲时,则会返回原始流,即 io.RawIOBase 的一个子类 io.FileIO。

如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:

>>> f.read()
'Hello, world!'
最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:

>>> f.close()
由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现:

每次都这么写实在太繁琐,所以,Python引入了with语句来自动帮我们调用close()方法:

调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。

如果已到达文件末尾,f.read() 将返回一个空字符串 (''):

readline() 从文件中读取一行;换行符(\n)留在字符串的末尾,如果文件不以换行符结尾,则在文件的最后一行省略。这使得返回值明确无误;如果 f.readline() 返回一个空的字符串,则表示已经到达了文件末尾,而空行使用 '\n' 表示,该字符串只包含一个换行符。:

第一次操作h2.txt结尾停在f第二行的file处,没有回车。第二次加了一个回车。

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:如果想以列表的形式读取文件中的所有行,也可以使用 list(f) 或 f.readlines()。

使用strip()函数,去掉末尾的'\n',所以第一次打印时,中间有一个空行,这是因为文件中每行末尾有一个换行符,而print()函数也会单独再加上一个换行符。第二次使用strip()去掉文件中的换行符,只有print的换行符,没有空行。

file-like Object:像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

二进制文件:默认都是读取文本文件,并且是UTF-8编码的文本文件。要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:

字符编码:要读文件的编码与open默认编码不一样时,需要给open()函数传入encoding参数,例如,读取GBK编码的文件:

>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
>>> f.read()
'测试'
遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:

>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')

写文件:
写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件,要写入特定编码的文本文件,请给open()函数传入encoding参数,将字符串自动转换成指定编码。

文件的编码是根据open()函数指定的,默认应该就是gbk,改为uft8:

带换行的写入:

可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险。

在写入其他类型的对象之前,需要先把它们转化为字符串(在文本模式下)或者字节对象(在二进制模式下):

这个结果跟想象的不一样,主要是对元组元素中的转义字符的处理。

以'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入'a'以追加(append)模式写入。

f.tell() 返回一个整数,给出文件对象在文件中的当前位置,表示为二进制模式下时从文件开始的字节数,以及文本模式下的意义不明的数字。

要改变文件对象的位置,请使用 f.seek(offset, whence)。 通过向一个参考点添加 offset 来计算位置;参考点由 whence 参数指定。 whence 的 0 值表示从文件开头起算,1 表示使用当前文件位置,2 表示使用文件末尾作为参考点。 whence 如果省略则默认值为 0,即使用文件开头作为参考点。

f.tell()就是从文件头开始到当前位置的字节数,f.seek()是移动的字节数,都是以字节来计算的。因为上面的例子是以文本方式打开,每次read(1),都是读了两个字节,因为文件默认是GBK编码。

seek()方法的格式file.seek(offset,whence),后面的1代表从当前位置开始算起进行偏移,那又为什么报错呢?

这是因为,在文本文件中,没有使用b模式选项打开的文件,只允许从文件头开始计算相对位置,从文件尾或当前位置计算时就会引发异常。将  f=open("aaa.txt","r")  改成

f = open("aaa.txt","rb")   就可以了

可以看到,更新也是在文件尾部,即追加,不能在文件中间插入。

在文本文件(那些在模式字符串中没有 b 的打开的文件)中,只允许相对于文件开头搜索(使用 seek(0, 2) 搜索到文件末尾是个例外)并且唯一有效的 offset 值是那些能从 f.tell() 中返回的或者是零。其他 offset 值都会产生未定义的行为。文件对象有一些额外的方法,例如 isatty() 和 truncate() 

StringIO
很多时候,数据读写不一定是文件,也可以在内存中读写。StringIO顾名思义就是在内存中读写str。

要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:

getvalue()方法用于获得写入后的str。要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:

BytesIO
StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

BytesIO实现了在内存中读写bytes,我们创建一个BytesIO,然后写入一些bytes:

注意,写入的不是str,而是经过UTF-8编码的bytes。和StringIO类似,可以用一个bytes初始化BytesIO,然后,像读文件一样读取

StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。

要操作文件、目录,可以在命令行下面输入操作系统提供的各种命令来完成。比如dir、cp等命令。

如果要在Python程序中执行这些目录和文件的操作怎么办?其实操作系统提供的命令只是简单地调用了操作系统提供的接口函数,Python内置的os模块也可以直接调用操作系统提供的接口函数。

如果是posix,说明系统是Linux、Unix或Mac OS X,如果是nt,就是Windows系统。要获取详细的系统信息,可以调用uname()函数。

uname()函数在Windows上不提供,也就是说,os模块的某些函数是跟操作系统相关的。

环境变量:在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看:

要获取某个环境变量的值,可以调用os.environ.get('key'):

操作文件和目录:
操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中,这一点要注意。查看、创建和删除目录可以这么调用:

把两个路径合成一个时,不要直接拼字符串,而要通过os.path.join()函数,这样可以正确处理不同操作系统的路径分隔符。在Linux/Unix/Mac下,os.path.join()返回这样的字符串:
part-1/part-2
而Windows下会返回这样的字符串:
part-1\part-2
同样的道理,要拆分路径时,也不要直接去拆字符串,而要通过os.path.split()函数,这样可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名。os.path.splitext()可以直接让你得到文件扩展名,很多时候非常方便:

这些合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作。

# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

利用Python的特性来过滤文件。比如我们要列出当前目录下的所有目录或要列出所有的.py文件,只需要一行代码:

序列化

在程序运行的过程中,所有的变量都是在内存中,比如,定义一个dict:
d = dict(name='Bob', age=20, score=88)
可以随时修改变量,比如把name改成'Bill',但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的'Bill'存储到磁盘上,下次重新运行程序,变量又被初始化为'Bob'。

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serializationmarshallingflattening等等,都是一个意思。

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling

Python提供了pickle模块来实现序列化。尝试把一个对象序列化并写入文件:

pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:

查看dump.txt文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息。

当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象:

变量的内容又回来了!当然,这个变量和原来的变量是完全不相干的对象,它们只是内容相同而已。

使用JSON保存结构化数据

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。 JSON (JavaScript Object Notation) ,json标准模块可以采用 Python 数据层次结构,并将它们转化为字符串表示形式;这个过程称为 serializing 。从字符串表示中重建数据称为 deserializing 。在序列化和反序列化之间,表示对象的字符串可能已存储在文件或数据中,或通过网络连接发送到某个远程机器。

JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

JSON类型Python类型
{}dict
[]list
"string"str
1234.56int或float
true/falseTrue/False
nullNone

 

 

 

 

 

 



Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。

dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。

要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化:

由于JSON标准规定JSON编码是UTF-8,所以总是能正确地在Python的str与JSON的字符串之间转换。

JSON进阶
Python的dict对象可以直接序列化为JSON的{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化:

出现错误TypeError: object of type Student is not JSON serializable
错误的原因是Student对象不是一个可序列化为JSON的对象。如果连class的实例对象都无法序列化为JSON,这肯定不合理!

仔细研究dumps()方法的参数列表,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数:

=========================================================================================================

json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
使用这个 转换表 将 obj 序列化为 JSON 格式化流形式的 fp (支持 .write() 的 file-like object)。

●  如果 skipkeys 是 true (默认为 False),那么那些不是基本对象(包括 str, int、float、bool、None)的字典的键会被跳过;否则引发一个 TypeError。json 模块始终产生 str 对象而非 bytes 对象。因此,fp.write() 必须支持 str 输入。
●  如果 ensure_ascii 是 true (即默认值),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。
●  如果 check_circular 是为假值 (默认为 True),那么容器类型的循环引用检验会被跳过并且循环引用会引发一个 OverflowError (或者更糟的情况)。
●  如果 allow_nan 是 false(默认为 True),那么在对严格 JSON 规格范围外的 float 类型值(nan、inf 和 -inf)进行序列化时会引发一个 ValueError。如果 allow_nan 是 true,则使用它们的 JavaScript 等价形式(NaN、Infinity 和 -Infinity)。
●  如果 indent 是一个非负整数或者字符串,那么 JSON 数组元素和对象成员会被美化输出为该值指定的缩进等级。如果缩进等级为 0、负数或者 "",则只会添加换行符。None``(默认值)选择最紧凑的表达。使用一个正整数会让每一层缩进同样数量的空格。如果 *indent* 是一个字符串(比如 ``"\t"),那个字符串会被用于缩进每一层。在 3.2 版更改: 现允许使用字符串作为 indent 而不再仅仅是整数。
●  当被指定时,separators 应当是一个 (item_separator, key_separator) 元组。当 indent 为 None 时,默认值取 (', ', ': '),否则取 (',', ': ')。为了得到最紧凑的 JSON 表达式,你应该指定其为 (',', ':') 以消除空白字符。在 3.4 版更改: 现当 indent 不是 None 时,采用 (',', ': ') 作为默认值。
●  当 default 被指定时,其应该是一个函数,每当某个对象无法被序列化时它会被调用。它应该返回该对象的一个可以被 JSON 编码的版本或者引发一个 TypeError。如果没有被指定,则会直接引发 TypeError。
●  如果 sort_keys 是 true(默认为 False),那么字典的输出会以键的顺序排序。
●  为了使用一个自定义的 JSONEncoder 子类(比如:覆盖了 default() 方法来序列化额外的类型), 通过 cls 关键字参数来指定;否则将使用 JSONEncoder。

在 3.6 版更改: 所有可选形参现在都是 仅限关键字参数。

注解:与 pickle 和 marshal 不同,JSON 不是一个具有框架的协议,所以尝试多次使用同一个 fp 调用 dump() 来序列化多个对象会产生一个不合规的 JSON 文件。

json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
使用这个 转换表 将 obj 序列化为 JSON 格式的 str。 其参数的含义与 dump() 中的相同。

注解:JSON 中的键-值对中的键永远是 str 类型的。当一个对象被转化为 JSON 时,字典中所有的键都会被强制转换为字符串。这所造成的结果是字典被转换为 JSON 然后转换回字典时可能和原来的不相等。换句话说,如果 x 具有非字符串的键,则有 loads(dumps(x)) != x。

json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
使用这个 转换表 将 fp (一个支持 .read() 并包含一个 JSON 文档的 text file 或者 binary file) 反序列化为一个 Python 对象。

● object_hook 是一个可选的函数,它会被调用于每一个解码出的对象字面量(即一个 dict)。object_hook 的返回值会取代原本的 dict。这一特性能够被用于实现自定义解码器。
● object_pairs_hook 是一个可选的函数,它会被调用于每一个有序列表对解码出的对象字面量。 object_pairs_hook 的返回值将会取代原本的 dict 。这一特性能够被用于实现自定义解码器。如果 object_hook 也被定义, object_pairs_hook 优先。在 3.1 版更改: 添加了对 object_pairs_hook 的支持。
● parse_float ,如果指定,将与每个要解码 JSON 浮点数的字符串一同调用。默认状态下,相当于 float(num_str) 。可以用于对 JSON 浮点数使用其它数据类型和语法分析程序 (比如 decimal.Decimal )。
● parse_int ,如果指定,将与每个要解码 JSON 整数的字符串一同调用。默认状态下,相当于 int(num_str) 。可以用于对 JSON 整数使用其它数据类型和语法分析程序 (比如 float )。
● parse_constant ,如果指定,将要与以下字符串中的一个一同调用: '-Infinity' , 'Infinity' , 'NaN' 。如果遇到无效的 JSON 数字则可以使用它引发异常。在 3.1 版更改: parse_constant 不再调用 'null' , 'true' , 'false' 。
● 要使用自定义的 JSONDecoder 子类,用 cls 指定他;否则使用 JSONDecoder 。额外的关键词参数会通过类的构造函数传递。
● 如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。
● 在 3.6 版更改: 所有可选形参现在都是 仅限关键字参数。
● 在 3.6 版更改: fp 现在可以是 binary file 。输入编码应当是 UTF-8 , UTF-16 或者 UTF-32 。

json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
使用这个 转换表 将 s (一个包含 JSON 文档的 str, bytes 或 bytearray 实例) 反序列化为 Python 对象。

除了*encoding*被忽略和弃用自 Python 3.1 以来,其他参数的含义与 load() 中相同。

如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。

在 3.6 版更改: s 现在可以为 bytes 或 bytearray 类型。 输入编码应为 UTF-8, UTF-16 或 UTF-32。

编码器和解码器
class json.JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None)
简单的JSON解码器。

默认情况下,解码执行以下翻译:

JSON

Python

object

dict

array

list

string

str

number (int)

int

number (real)

float

true

True

false

False

null

None

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

decode(s)
返回 s 的 Python 表示形式(包含一个 JSON 文档的 str 实例)。如果给定的 JSON 文档无效则将引发 JSONDecodeError。

raw_decode(s)
从 s 中解码出 JSON 文档(以 JSON 文档开头的一个 str 对象)并返回一个 Python 表示形式为 2 元组以及指明该文档在 s 中结束位置的序号。这可以用于从一个字符串解码JSON文档,该字符串的末尾可能有无关的数据。

 

class json.JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)
用于Python数据结构的可扩展JSON编码器。

默认支持以下对象和类型:

Python

JSON

dict

object

list, tuple

array

str

string

int, float, int 和 float 派生的枚举

number

True

true

False

false

None

null








 

 

 

 



default(o)
在子类中实现这种方法使其返回 o 的可序列化对象,或者调用基础实现(引发 TypeError )。

encode(o)
返回 Python o 数据结构的 JSON 字符串表达方式。

iterencode(o)
编码给定对象 o ,并且让每个可用的字符串表达方式。

====================================================================

可选参数就是让我们来定制JSON序列化。前面的代码之所以无法把Student类实例序列化为JSON,是因为默认情况下,dumps()方法不知道如何将Student实例变为一个JSON的{}对象。

可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:

下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。可以把任意class的实例变为dict:

因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。

同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例:

打印出的是反序列化的Student实例对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值