首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Python基础教程 shelve和json

10.3.7 shelve 和 json

下一章将介绍如何将数据存储到文件中,但如果需要的是简单的存储方案,模块shelve可替你完成大部分工作——你只需提供一个文件名即可。对于模块shelve,你唯一感兴趣的是函数open。这个函数将一个文件名作为参数,并返回一个Shelf对象,供你用来存储数据。你可像操作普通字典那样操作它(只是键必须为字符串),操作完毕(并将所做的修改存盘)时,可调用

其方法close。

1. 一个潜在的陷阱

至关重要的一点是认识到shelve.open返回的对象并非普通映射,如下例所示:

>>> import shelve

>>> s = shelve.open('test.dat')

>>> s['x'] = ['a', 'b', 'c']

>>> s['x'].append('d')

>>> s['x']

['a', 'b', 'c']

'd'到哪里去了呢?

这很容易解释:当你查看shelf对象中的元素时,将使用存储版重建该对象,而当你将一个元素赋给键时,该元素将被存储。在上述示例中,发生的事情如下。

 列表['a', 'b', 'c']被存储到s的'x'键下。

 获取存储的表示,并使用它创建一个新列表,再将'd'附加到这个新列表末尾,但这个修改后的版本未被存储!

 最后,再次获取原来的版本——其中没有'd'。

要正确地修改使用模块shelve存储的对象,必须将获取的副本赋给一个临时变量,并在修改这个副本后再次存储①:

>>> temp = s['x']

>>> temp.append('d')

>>> s['x'] = temp

>>> s['x']

['a', 'b', 'c', 'd']

还有另一种避免这个问题的办法:将函数open的参数writeback设置为True。这样,从shelf对象读取或赋给它的所有数据结构都将保存到内存(缓存)中,并等到你关闭shelf对象时才将它们写入磁盘中。如果你处理的数据不多,且不想操心这些问题,将参数writeback设置为True可能是个不错的主意。在这种情况下,你必须确保在处理完毕后将shelf对象关闭。为此,一种办法是像处理打开的文件那样,将shelf对象用作上下文管理器,这将在下一章讨论。

2. 一个简单的数据库示例

代码清单10-8是一个使用模块shelve的简单数据库应用程序。

代码清单10-8 一个简单的数据库应用程序

# database.py

import sys, shelve

def store_person(db):

"""

让用户输入数据并将其存储到shelf对象中

"""

pid = input('Enter unique ID number: ')

person = {}

person['name'] = input('Enter name: ')

person['age'] = input('Enter age: ')

person['phone'] = input('Enter phone number: ')

db[pid] = person

def lookup_person(db):

"""

让用户输入ID和所需的字段,并从shelf对象中获取相应的数据

"""

pid = input('Enter ID number: ')

field = input('What would you like to know? (name, age, phone) ')

field = field.strip().lower()

print(field.capitalize() + ':', db[pid][field])

def print_help():

print('The available commands are:')

print('store : Stores information about a person')

print('lookup : Looks up a person from ID number')

print('quit : Save changes and exit')

print('? : Prints this message')

def enter_command():

cmd = input('Enter command (? for help): ')

cmd = cmd.strip().lower()

return cmd

def main():

database = shelve.open('C:\\database.dat') # 你可能想修改这个名称

try:

while True:

cmd = enter_command()

if cmd == 'store':

store_person(database)

elif cmd == 'lookup':

lookup_person(database)

elif cmd == '?':

print_help()

elif cmd == 'quit':

return

finally:

database.close()

if name == '__main__': main()

代码清单10-8所示的程序有几个有趣的特征。

 所有代码都放在函数中,这提高了程序的结构化程度(一个可能的改进是将这些函数作为一个类的方法)。

 主程序位于函数main中,这个函数仅在__name__== '__main__'时才会被调用。这意味着可在另一个程序中将这个程序作为模块导入,再调用函数main。

 在函数main中,我打开一个数据库(shelf),再将其作为参数传递给其他需要它的函数。由于这个程序很小,我原本可以使用一个全局变量,但在大多数情况下,最好不要使用全局变量——除非你有理由这样做。

 读入一些值后,我调用strip和lower来修改它们,因为仅当提供的键与存储的键完全相同时,它们才匹配。如果对用户输入的内容都调用strip和lower,用户输入时就无需太关心大小写,且在输入开头和末尾有多余的空白也没有关系。另外,注意到打印字段名时使用了capitalize。

 为确保数据库得以妥善的关闭,我使用了try和finally。不知道什么时候就会出现问题,进而引发异常。如果程序终止时未妥善地关闭数据库,数据库文件可能受损,变得毫无用处。通过使用try和finally,可避免这样的情况发生。我原本也可像第11章介绍的那样,将shelf用作上下文管理器。

我们来试试这个数据库。下面是一个示例交互过程:

Enter command (? for help): ?

The available commands are:

store : Stores information about a person

lookup : Looks up a person from ID number

quit : Save changes and exit

? : Prints this message

Enter command (? for help): store

Enter unique ID number: 001

Enter name: Mr. Gumby

Enter age: 42

Enter phone number: 555-1234

Enter command (? for help): lookup

Enter ID number: 001

What would you like to know? (name, age, phone) phone

Phone: 555-1234

Enter command (? for help): quit

这个交互过程并不是很有趣。我原本可以使用普通字典(而不是shelf对象)来完成这个任务。退出这个程序后,来看看再次运行它时(这也许是在第二天)发生的情况。

Enter command (? for help): lookup

Enter ID number: 001

What would you like to know? (name, age, phone) name

Name: Mr. Gumby

Enter command (? for help): quit

如你所见,这个程序读取前面运行它时创建的文件,该文件依然包含Mr. Gumby!

请随便实验这个程序,看看你能否扩展其功能并让它对用户更友好。你或许能够设计出一个可为你所用的版本。

提示 如果要以这样的格式保存数据,也就是让使用其他语言编写的程序能够轻松地读取它们,可考虑使用JSON格式。 Python标准库提供了用于处理JSON字符串(在这种字符串和Python值之间进行转换)的模块json。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190122G1AGVY00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券