返回介绍

5.1 存储

发布于 2023-06-02 10:04:34 字数 19567 浏览 0 评论 0 收藏 0

1.文件

我们知道,Python中的数据都保存在内存中。当电脑断电时,就好像患了失忆症,内存中的数据就会消失。另一方面,如果Python程序运行结束,那么分配给这个程序的内存空间也会清空。为了长期持续地存储,Python必须把数据存储在磁盘中。这样,即使断电或程序结束,数据依然存在。

磁盘以文件为单位来存储数据。对于计算机来说,数据的本质就是有序的二进制数序列。如果以字节为单位,也就是每8位二进制数序列为单位,那么这个数据序列就称为文本。这是因为,8位的二进制数序列正好对应ASCII编码中的一个字符。而Python能够借助文本对象来读写文件。

在Python中,我们可以通过内置函数open来创建文件对象。在调用open时,需要说明文件名,以及打开文件的方式:


f = open(文件名,方式)

文件名是文件存在于磁盘的名字,打开文件的常用方式有:


"r" # 读取已经存在的文件
"w" # 新建文件,并写入
"a" # 如果文件存在,那么写入到文件的结尾。如果文件不存在,则新建文件并写入

例如:


>>>f = open("test.txt","r")

就是用只读的方式,打开了一个名为test.txt的文件。

通过上面返回的对象,我们可以读取文件:


content = f.read(10)         # 读取10个字节的数据
content = f.readline()       # 读取一行
content = f.readlines()      # 读取所有行,储存在列表中,每个元素是一行。

如果以"w"或"a"方式打开,那么我们可以写入文本:


f = open("test.txt", "w")
f.write("I like apple")      # 将"I like apple"写入文件

如果想写入一行,则需要在字符串末尾加上换行符。在UNIX系统中,换行符为"\n"。在Windows系统中,换行符为"\r\n"。


f.write("I like apple\n")    # UNIX
f.write("I like apple\r\n")  # Windows

打开文件端口将占用计算机资源,因此,在读写完成后,应该及时的用文件对象的close方法关闭文件:


f.close()

2.上下文管理器

文件操作常常和上下文管理器一起使用。上下文管理器(context manager)用于规定某个对象的使用范围。一旦进入或者离开该使用范围,则会有特殊操作被调用,比如为对象分配或者释放内存。上下文管理器可用于文件操作。对于文件操作来说,我们需要在读写结束时关闭文件。程序员经常会忘记关闭文件,无谓的占用资源。上下文管理器可以在不需要文件的时候,自动关闭文件。

下面是一段常规的文件操作程序:


# 常规文件操作
f = open("new.txt", "w")
print(f.closed)               # 检查文件是否打开
f.write("Hello World!")
f.close()

print(f.closed)               # 打印True

如果我们加入上下文管理器的语法,就可以把程序改写为:


# 使用上下文管理器
with open("new.txt", "w") as f:
    f.write("Hello World!")

print(f.closed)

第二段程序就使用了with...as...结构。上下文管理器有隶属于它的程序块,当隶属的程序块执行结束时,也就是语句不再缩进时,上下文管理器就会自动关闭文件。在程序中,我们调用了f.closed属性来验证是否已经关闭。通过上下文管理器,我们相当于用缩进来表达文件对象的打开范围。对于复杂的程序来说,缩进的存在能让程序员更清楚地意识到文件在哪些阶段打开,减少忘记关闭文件的可能性。

上面的上下文管理器基于f对象的__exit__()特殊方法。使用上下文管理器的语法时,Python会在进入程序块之前调用文件对象的__enter__()方法,在结束程序块的时候调用文件对象的__exit__()方法。在文件对象的__exit__()方法中,有self.close()语句。因此,在使用上下文管理器时,我们就不用明文关闭文件了。

任何定义了__enter__()方法和__exit__()方法的对象都可以用于上下文管理器。下面,我们自定义一个类Vow,并定义它的__enter__()方法和__exit__()方法。因此,由Vow类的对象可以用于上下文管理器:


class Vow(object):
    def __init__(self, text):
        self.text = text
    def __enter__(self):
        self.text = "I say: " + self.text    # 增加前缀
        return self                          # 返回一个对象
    def __exit__(self,exc_type,exc_value,traceback):
        self.text = self.text + "!"          #增加后缀

with Vow("I'm fine") as myVow:
    print(myVow.text)

print(myVow.text)

运行结果如下:


I say: I'm fine
I say: I'm fine!

初始化对象时,对象的text属性是"I'm fine"。我们可以看到,在进入上下文和离开上下文时,对象调用了__enter__()方法和__exit__()方法,从而造成对象的text属性改变。

__enter__()返回一个对象。上下文管理器会使用这一对象作为as所指的变量。我们自定义的__enter__()返回的是self,也就是新建的Vow类对象本身。在__enter__()中,我们为text属性增加了前缀"I say:"。在__exit__()中,我们为text属性增加了后缀"!"。

值得注意的是, __exit__()有四个参数。当程序块中出现异常时,__exit__()参数中的exc_type、exc_value、traceback用于描述异常。我们可以根据这三个参数进行相应的处理。如果正常运行结束,则这三个参数都是None。

3.pickle包

我们能把文本存于文件。但Python中最常见的是对象,当程序结束或计算机关机时,这些存在于内存的对象会消失。那么,我们能否把对象保存到磁盘上呢?

利用Python的pickle包就可以做到这一点。英文里,pickle是腌菜的意思。大航海时代的海员们常把蔬菜做成腌菜,装在罐头里带着走。Python中的pickle也有类似的意思。通过pickle包,我们可以把某个对象保存下来,再存成磁盘里的文件。

实际上,对象的存储分为两步。第一步,我们将对象在内存中的数据直接抓取出来,转换成一个有序的文本,即所谓的序列化(Serialization)。第二步,将文本存入文件。等到需要时,我们从文件中读出文本,再放入内存,就可以获得原有的对象。下面是一个具体的例子,首先是第一步序列化,将内存中的对象转换为文本流:


import pickle

class Bird(object):
have_feather = True
reproduction_method  = "egg"

summer        = Bird()                 # 创建对象
pickle_string = pickle.dumps(summer)   # 序列化对象

使用pickle包的dumps()方法可以将对象转换成字符串的形式。随后我们用字节文本的存储方法,将该字符串储存在文件。继续第二步:


with open("summer.pkl", "wb") as f:
    f.write(pickle_string)

上面程序故意分成了两步,以便更好地展示整个过程。其实,我们可以使用dump()的方法,一次完成两步:


import pickle

class Bird(object):
    have_feather = True
    reproduction_method  = "egg"

summer = Bird()
with open("summer.pkl", "w") as f:
    pickle.dump(summer, f)         # 序列化并保存对象

对象summer将存储在文件summer.pkl中。有了这个文件,我们就可以在必要的时候读取对象了。读取对象与存储对象的过程正好相反。首先,我们从文件中读出文本。然后使用pickle的loads()方法,将字符串形式的文本转换为对象。我们也可以使用pickle的load()的方法,将上面两步合并。

有时候,仅仅是反向恢复还不够。对象依赖于它的类,所以Python在创建对象时,需要找到相应的类。因此当我们从文本中读取对象时,程序中必须已经定义过类。对于Python总是存在的内置类,如列表、词典、字符串等,不需要再在程序中定义。但对于用户自定义的类,就必须要先定义类,然后才能从文件中载入该类的对象。下面是一个读取对象的例子:


import pickle

class Bird(object):
    have_feather          = True
    reproduction_method   = "egg"

with open("summer.pkl", "rb") as f:
    summer = pickle.load(f)

print(summer.have_feather)                         # 打印True

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文