第二十五节 存储数据

10.4存储数据

很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供要可视化的数据。不管关注点是什么,程序都把用户提供的信息存储在列表和字典等数据结构中。用户关闭程序时,几乎总是要保存他们提供的信息。一种简单的方式是使用模块json来存储数据。

模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用json在Python程序之间分享数据。更重要的是,JSON数据格式并非Python专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。这是一种轻便而有用的格式,也易于学习。

注意JSON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python在内的众多语言采用。

10.4.1 使用json.dump() 和json.load()

我们来编写一个存储一组数的简短程序,再编写一个将这些数读取到内存中的程序。第一个程序将使用json.dump() 来存储这组数,而第二个程序将使用json.load() 。

函数json.dump() 接受两个实参:要存储的数据,以及可用于存储数据的文件对象。下面演示了如何使用json.dump() 来存储数字列表:

number_writer.py

import json
numbers = [2, 3, 5, 7, 11, 13]
filename = 'numbers.json' ❶
with open(filename, 'w') as f:❷
    json.dump(numbers, f) ❸

先导入模块json ,再创建一个数字列表。在❶处,指定了要将该数字列表存储到哪个文件中。通常使用文件扩展名.json来指出文件存储的数据为JSON格式。接下来, 以写入模式打开这个文件,让json  能够将数据写入其中(见❷)。在❸处,使用函数json.dump() 将数字列表存储到文件numbers.json中。

这个程序没有输出,但可以打开文件numbers.json来看看内容。数据的存储格式与Python中一样:

[2, 3, 5, 7, 11, 13]

下面再编写一个程序,使用json.load() 将列表读取到内存中:

number_reader.py

import json

numbers = [2, 3, 5, 7, 11, 13]
filename = 'numbers.json' ❶
with open(filename) as f: ❷
    number = json.load(f) ❸

print(number)

在❶处,确保读取的是前面写入的文件。这次以读取方式打开该文件,因为Python只需要读取它(见❷)。在❸处,使用函数json.load() 加载存储在numbers.json中的信息,并将其赋给变量numbers 。最后,打印恢复的数字列表,看看是否与number_writer.py中创建的数字列表相同:

[2, 3, 5, 7, 11, 13]

这是一种在程序之间共享数据的简单方式。

10.4.2 保存和读取用户生成的数据

使用json保存用户生成的数据大有裨益,因为如果不以某种方式存储,用户的信息会在程序停止运行时丢失。下面来看一个这样的例子:提示用户首次运行程序时输入自己的名字,并在再次运行程序时记住他。

先来存储用户的名字:

remember_me.py

import json

username = input("What is your name? ")❶

filename = 'username.json'
with open(filename, 'w') as f: ❷
    json.dump(username, f) ❸

    print(f"We'll remember you when you come back, {username}!")

在❶处,提示输入用户名并将其赋给一个变量。接下来,调用json.dump() ,并将用户名和一个文件对象传递给它,从而将用户名存储到文件中(见❷)。然后,打印一条消息,指出存储了用户输入的信息(见❸):

What is your name? Sun
We'll remember you when you come back, Sun!

现在再编写一个程序,向已存储了名字的用户发出问候:

greet_user.py

import json

filename = 'username.json'

with open(filename) as f:
    username = json.load(f) ❶

    print(f"Welcome back, {username}!")❷

在❶处,使用json.load() 将存储在username.json中的信息读取到变量username 中。恢复用户名后,就可以欢迎用户回来了(见❷):

Welcome back, Eric!

需要将这两个程序合并到一个程序(remember_me.py)中。这个程序运行时,将尝试从文件username.json中获取用户名。因此,首先编写一个尝试恢复用户名的try代码块。如果这个文件不存在,就在except代码块中提示用户输入用户名,并将其存储到username.json中,以便程序再次运行时能够获取:

remember_me.py

import json
# 如果以前存储了用户名,就加载它。
# 否则,提示用户输入用户名并存储它。
filename = 'username.json'
try:
    with open(filename) as f: ❶
        username = json.load(f) ❷
except FileNotFoundError: ❸
    username = input("What is your name? ") ❹
    with open(filename, 'w') as f: ❺
        json.dump(username, f)
        print(f"We'll remember you when you come back, {username}!")
else:
    print(f"Welcome back, {username}!")

这里没有任何新代码,只是将前两个示例的代码合并到了一个程序中。在❶处,尝试打开文件username.json。如果该文件存在,就将其中的用户名读取到内存中(见❷),再执行else代码块,打印一条欢迎用户回来的消息。用户首次运行该程序时,文件username.json不存在,将引发FileNotFoundError 异常(见❸)。因此Python将执行except 代码块,提示用户输入用户名(见❹),再使用json.dump() 存储该用户名并打印一句问候语(见❺)。

无论执行的是except 还是else 代码块,都将显示用户名和合适的问候语。如果这个程序是首次运行,输出将如下:

What is your name? Eric
We'll remember you when you come back, Eric!

否则,输出将如下:

Welcome back, Eric!

这是程序之前至少运行了一次时的输出。

10.4.3 重构

你经常会遇到这样的情况:代码能够正确地运行,但通过将其划分为一系列完成具体工作的函数,还可以改进。这样的过程称为重构。重构让代码更清晰、更易于理解、更容易扩展。

要重构remember_me.py,可将其大部分逻辑放到一个或多个函数中。remember_me.py的重点是问候用户,因此将其所有代码都放到一个名为greet_user() 的函数中:

remember_me.py

import json
def greet_user():
    """问候用户,并指出其名字。"""❶
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        username = input("What is your name? ")
        with open(filename, 'w') as f:
            json.dump(username, f)
            print(f"We'll remember you when you come back, {username}!")
    else:
        print(f"Welcome back, {username}!")
greet_user()

考虑到现在使用了一个函数,我们删除原注释,转而使用一个文档字符串来指出程序的作用(见❶)。这个程序更加清晰,但函数greet_user() 所做的不仅仅是问候用户,还在存储了用户名时获取它、在没有存储用户名时提示用户输入。

下面来重构greet_user() ,减少其任务。为此,首先将获取已存储用户名的代码移到另一个函数中:

import json
def get_stored_username():
    """如果存储了用户名,就获取它。"""❶
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None ❷
    else:
        return username
    
def greet_user():
    """问候用户,并指出其名字。"""
    username = get_stored_username()
    if username: ❸
        print(f"Welcome back, {username}!")
    else:
        username = input("What is your name? ")
        filename = 'username.json'
        with open(filename, 'w') as f:
            json.dump(username, f)
            print(f"We'll remember you when you come back, {username}!")
greet_user()

新增的函数get_stored_username() 目标明确,❶处的文档字符串指出了这一点。如果存储了用户名,该函数就获取并返回它;如果文件username.json不存在, 该函数就返回None (见❷)。这是一种不错的做法:函数要么返回预期的值,要么返回None 。这让我们能够使用函数的返回值做简单的测试。在❸处,如果成功地获取了用户名,就打印一条欢迎用户回来的消息,否则提示用户输入用户名。

还需要重构greet_user() 中的另一个代码块,将没有存储用户名时提示用户输入的代码放在一个独立的函数中:

import json
def get_stored_username():
    """如果存储了用户名,就获取它。"""
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None
    else:
        return username

def get_new_username():
    """提示用户输入用户名。"""
    username = input("What is your name? ")
    filename = 'username.json'
    with open(filename, 'w') as f:
        json.dump(username, f)
    return username

    
def greet_user():
    #问候用户,并指出其名字。
    username = get_stored_username()
    if username:
        print(f"Welcome back,{username}!")
    else:
        username = get_new_username()
        print(f"We'll remember you when you come back,{username}!")
greet_user()

在remember_me.py的这个最终版本中,每个函数都执行单一而清晰的任务。我们调用greet_user() ,它打印一条合适的消息:要么欢迎老用户回来,要么问候新用户。为此,它首先调用get_stored_username() ,该函数只负责获取已存储的用户名(如果存储了的话)。最后在必要时调用get_new_username() ,该函数只负责获取并存储新用户的用户名。要编写出清晰而易于维护和扩展的代码,这种划分必不可少。

动手试一试

练习10-11:喜欢的数

编写一个程序,提示用户输入喜欢的数,并使用json.dump() 将这个数存储到文件中。再编写一个程序,从文件中读取这个值,并打印如下所示的消息。

I know your favorite number! It's _____.

import json
filename = 'favorite_number.json'
number = input("请输入一个你喜欢的数字:")
with open(filename,'w') as f:
    json.dump(number,f)
with open(filename) as f:
    favorite_number = json.load(f)
    print(f"I know your favorite number. It's {favorite_number}.")

练习10-12:记住喜欢的数

将练习10-11中的程序合二为一。如果存储了用户喜欢的数,就向用户显示它,否则提示用户输入喜欢的数并将其存储到文件中。运行这个程序两次,看看它能否像预期的那样工作。

import json
filename = 'favorite_number.json'
try:
    with open(filename) as f:
        favorite_number = json.load(f)
except FileNotFoundError:
    favorite_number = input("请输入一个你喜欢的数字:")
    with open(filename,'w') as f:
        json.dump(favorite_number,f)
print(f"I know your favorite number. It's {favorite_number}.")

练习10-13:验证用户  

最后一个remember_me.py版本假设用户要么已输入用户名,要么是首次运行该程序。我们应该修改这个程序,以防当前用户并非上次运行该程序的用户。

为此,在greet_user() 中打印欢迎用户回来的消息前,询问他用户名是否正确。如果不对,就调用get_new_username() 让用户输入正确的用户名。

import json
def get_stored_username():
    #如果存储了用户名,就获取它。
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None
    else:
        confirmed = input(f"Is your name {username}?(yes/no)")
        if confirmed == "no":
            username = input("Please input your name. ")
        return username

def get_new_username():
    #提示用户输入用户名。
    username = input("What's your name? ")
    filename = 'username.json'
    with open(filename,'w') as f:
        json.dump(username,f)
    return username

def greet_user():
    #问候用户,并指出其名字。
    username = get_stored_username()
    if username:
        print(f"Welcome back,{username}!")
    else:
        username = get_new_username()
        print(f"We'll remember you when you come back,{username}!")
greet_user()

10.5 小结

在本章中,你学习了:

如何使用文件;

如何一次性读取整个文件,以及如何以每次 一行的方式读取文件的内容;

如何写入文件,以及如何将文本附加到文件末尾;

什么是异常以及如何处理程序可能引发的异常;

如何存储Python数据结构,以保存用户提供的信息,避免用户每次运行程序时都需要重新提供。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值