本教程包括:
- 什么是Python数据结构以及如何使用它们
- 如何实现一个由Python数据结构驱动的API
- 如何对你的Python API进行自动化测试
因为计算机依靠数据来执行指令,所以计算总是需要数据交互。在现实世界的应用中,数据量可能是压倒性的,所以开发者必须不断设计方法,以编程的方式快速有效地访问它。
对于专门开发工具和系统的团队来说,对数据结构的扎实理解是一个很大的优势。对数据进行优化组织可以最大限度地提高效率,使数据处理变得简单和无缝。在本教程中,你将学习Python中的数据结构,如何使用它们来构建高效和高性能的应用程序,以及如何使用持续集成为你的Python应用程序进行自动测试。
先决条件
要完成本教程,需要以下条件:
- 在您的系统上安装Python 3+
- 一个CircleCI账户
- 一个GitHub账户和使用Git和GitHub的知识
- 一个HTTP客户端;Postman或Insomnia都行
- 对Python和Flask的了解
- 了解API的工作原理
什么是数据结构?
数据结构是一种组织和管理内存中的数据的方法,以有效地执行对该数据的操作。使用不同的数据类型构建数据结构,以及定义保存数据的变量,都有最佳实践。
为什么需要Python数据结构?
随着系统复杂性的增加,数据也在增加。今天,处理大量的数据经常导致处理器速度的问题,搜索和排序数据的效率低下,还有处理多个用户请求时的问题。这些问题对性能至关重要,它们必须得到解决,以实现任何系统的最大效率。
数据结构被用来决定一个程序或一个系统如何运行。按顺序搜索数组中的数据将耗费大量的时间和资源。这可以通过使用数据结构(如哈希表)来解决。
数据抽象隐藏了数据结构的复杂细节,这样客户程序就不必知道实现细节。这是通过抽象数据类型完成的,它为你的应用程序提供了抽象性。
Python 中的数据结构概述
Python 提供了内置的数据结构,如列表、字典、集合和图元。Python 用户可以创建自己的数据结构并最终控制它们的实现方式。堆栈、队列、树、链表和图是用户定义的数据结构的例子。
本教程将重点介绍列表和字典,以及开发者如何使用它们来优化应用程序中的数据存储和检索。
下一节的重点是使用列表和字典的优化操作来存储、处理和检索数据结构中的数据。
列表
列表是一个元素的有序集合。因为列表是可变的,它们的值可以改变。项目是包含在一个列表中的值。
注意 :Python 数据结构的类型决定了它的可变性。可变的对象可以改变其状态或内容,而不可变的对象不能。
你通过使用方括号来表示 Python 列表。这里是一个空列表的例子。
categories = [ ]
逗号 (,) 用来分隔列表中的项目。
categories = [ science, math, physics, religion ]
列表也可以包含属于列表的项目。
scores = [ [23, 45, 60] , [67, 69, 90] ]
index ,它只不过是一个列表中数值的位置,是用来访问列表中的元素。下面是一个如何访问列表中各种项目的例子。
categories = [ science, math, physics, religion ]
输出
categories [0] # science
categories [1] # math
categories [2] # physics
你也可以使用负数索引从列表的末尾开始访问项目。例如,要访问前面列表中的最后一个项目。
categories [-1] # religion
你可以添加、删除和修改列表中的项目,因为列表是可变的。
要改变列表中某一项目的值,需要引用该项目的位置,然后使用赋值运算符。
categories [ 0 ] = “geography” # modifies the lists, replacing “science” with “geography”
要向列表中添加新的项目,可以使用append() 方法,该方法将项目添加到列表的末尾。
categories .append( “linguistics” )
另一个可以在列表中使用的方法是insert() ,它在列表中的一个随机位置添加项目。其他列表对象包括del(),pop(),clear(), 和sort() 。
字典(Dictionaryaries
字典是 Python 中内置的key-value 对数据类型的集合。与列表不同,字典是由键来索引的,它可以是字符串、数字或图元。一般来说,一个字典的键可以是任何不可变的类型。
一个 dictionary 的 key 必须是不同的。大括号{} 是用来表示字典的。
键使字典的工作变得简单,也可以存储各种类型的数据,包括列表或甚至其他字典。你可以使用字典的键来访问、删除和执行其他操作。关于字典,需要记住的一件重要事情是,用一个已经存在的键来存储数据将覆盖以前与该键相关的值。
下面是一个使用字典的例子。
student = { “name”: “Mike”, “age”: 24, “grade”: “A” }
要访问上述字典内的项目。
student[ ‘name’ ] # Mike
向 dictionary 中添加数据就像 Dict[key] = value 一样简单。
student[ ‘subjects’ ] = 7
Python 的字典方法包括len(),pop(),index(),len(), 和popitem() 。
下面这些常用的方法允许它从 dictionary 中返回值。
dict.items() # return key-value pairs as a tuple
dict.keys() # returns the dictionary's keys
dict.get(key) # returns the value for the specified key and returns None if the key cannot be found.
下图显示了不同类型的 Python 数据结构,包括内置的和用户定义的。

在本教程的下面一节中,你将使用刚刚获得的知识来创建一个简单的 API,允许你存储、操作和检索数据结构。
API流程图和存储
现在你知道什么是列表和字典了,你可以用它们来创建一个具有登录功能的API端点,数据只存储在数据结构中。你可以观察数据将如何在应用程序中流动以及你将如何在你的API中使用数据结构。

这个API图显示了一个数据存储,它是一个Python字典。它被初始化为样本用户数据,因为这允许你充分探索数据结构的能力。步骤被标记为1 到4 ,以显示数据在API中的流动。
第一步让用户通过输入他们的first name 、last name 、username 和date of birth 来创建账户。这些细节被保存在users 字典中。
第二步是检索系统中的所有用户。在发回数据之前,它被从一个嵌套的字典转换为一个排序的列表。
第三步用一个Id 和一个username 来验证一个用户。
第四步是数据结构在向客户端发回响应之前进行实际处理。
现在你知道了你的API将如何工作,你可以把数据结构放到一个实际的应用程序中。
用数据结构实现一个API
使用数据结构实现包括以下步骤。
- 设置一个API骨架
- 初始化用户
- 创建用户
- 检索用户
设置API骨架
为了继续学习本教程,我鼓励你克隆该应用程序。这样,你就可以浏览应用程序,了解本教程中没有完全记录的部分。
git clone https://github.com/CIRCLECI-GWP/python-api-with-datastructures
cd python-api-with-datastructures
为了安装Python依赖项,你需要使用这些命令建立一个虚拟环境。
Windows操作系统
py -3 -m venv venv;
venv\Scripts\activate;
Linux/MacOS
python3 -m venv venv
source venv/bin/activate
安装requirements.txt文件中的要求。
pip install -r requirements.txt
要启动API,请运行。
python main.py
设置和启动API骨架的工作非常出色下一步是修改你的路由,并创建一个链接列表来处理用户认证和数据转换。
初始化用户
考虑到你的应用状态只在服务器运行时持续,你将创建一个用户字典,用样本数据进行初始化。要做到这一点,在main.py 文件中手动添加数据到用户的字典中,就在Flask应用配置之后。
这就是修改后的字典应该有的样子。
# main.py
users = {
1: {"fname": "John", "lname": "Doe", "username": "John96", "dob": "08/12/2000"},
2: {
"fname": "Mike",
"lname": "Spencer",
"username": "miker5",
"dob": "01/08/2004",
},
}
现在,即使你的服务器停止了,在你测试端点或创建新的应用程序数据时,你将始终有内存中的数据可以参考。
创建用户
在初始化了用户数据字典之后,制作一个create user 函数来创建你的用户。使用request库,因为这将是一个API请求,而用户凭证将通过提交的方式进入。
使用请求库中的get_json() 方法--data = request.get_json() --来解析传入的JSON请求数据,并将其存储在一个变量中。任何系统都不应该允许重复的记录,你的API也不例外。因此,当创建一个新用户时,要确保新用户的详细信息与任何可用的记录不匹配。如果相同的数据已经可用,则通知用户并停止该过程。复制这段代码并将其粘贴到main.py 文件中。
# main.py
@app.route("/user", methods=["POST"])
def create_user():
data = request.get_json()
if data["id"] not in users.keys():
users[data["id"]] = {
"fname": data["fname"],
"lname": data["lname"],
"username": data["username"],
"dob": data["dob"],
}
else:
return jsonify({"message": "user already exists"}), 401
return jsonify({"message": "user created"}), 201
这个代码块首先确定用户id ,通过在用户字典的键中搜索类似的id,确定用户是否已经被存储在用户数据存储中。检查是否有id ,这不是程序性的;相反,你可以在生产中检查用户的电子邮件。
如果该检查通过,新的用户信息就被输入到字典中,使用唯一的用户 ID 作为键。当字典针对用户id存储时,这种模式会产生一个嵌套的字典。
Flask包含一个叫jsonify 的函数,允许你将数据序列化为JSON格式,你将用它来格式化发回客户端的信息。
检索用户
检索用户可以像返回users 字典一样简单,但有一个更好的方法。相反,为什么不按降序返回所有的用户,把最近创建的用户放在最上面?
不幸的是,字典在 Python 3 中不再是可排序的,所以它们不能被排序。相反,你可以使用这个片段。
# main.py
@app.route("/users", methods=["GET"])
def get_users():
all_users = []
for key in users:
all_users.append(users[key])
users[key]["id"] = key
all_users.sort(key=lambda x: x["id"], reverse=True)
return jsonify(users), 200
这在前面的代码块中创建了一个空列表,然后循环浏览users 的字典值,把每个值追加到列表中。另外,每个用户都需要一个唯一的标识符,所以在列表中追加一个id 是一个好主意。
记住,在追加到列表之后,你有一个字典的列表,你不能通过将你的嵌套字典转换为字典的列表来欺骗 Python。这就是为什么你应该使用 lambda 函数来指定 id 作为排序方法的一个键。结果是一个按用户的id 值降序排序的字典列表。
最后,添加认证功能--/user/login ,在创建用户并实现按顺序检索用户的函数后,将是非常好的。
# main.py
app.route("/users/login", methods=["POST"])
def login_user():
data = request.get_json()
id = data["id"]
username = data["username"]
if id in users.keys():
if users[id]["username"] == username:
return jsonify(f"Welcome, you are logged in as {username}"), 200
return jsonify("Invalid login credentials"), 401
在使用id 和username ,通过比较发出的id和记录,确保这样的用户存在。如果存在匹配,你可以验证用户名。如果一个用户输入了有效的登录信息,就把他们登录进去,并显示一个带有他们用户名的欢迎信息。相反,如果登录失败,将简单地显示一条消息,通知他们登录失败。
开始测试你刚刚创建的三个端点。create a user,log them in, 和retrieve all users added 。如果有什么问题,你可以随时参考位于克隆版本库中的main.py 文件。
创建用户的API调用

登录用户的API调用

检索所有用户的API调用

利用在列表和字典中存储数据的能力,你可以验证API是否按预期工作。
为你的API编写测试
没有经过测试的代码已经被破坏了 可能很乏味,也很耗时,但在一个应用程序中添加测试从来没有真正的损失。本教程的这一部分包括对用户创建、多用户创建、登录和用户检索你刚刚创建的API端点的测试。我将指导你使用
Pytest,一个Python应用测试工具来测试你的端点。你要写的第一个测试是创建一个用户的测试。
# tests/test_app.py
def test_create_user(client):
response = client.post(
"/user",
json={
"id": 4,
"fname": "James",
"lname": "Max",
"username": "Maxy",
"dob": "08/12/2000",
},
)
assert response.headers["Content-Type"] == "application/json"
assert response.status_code == 201
这段代码创建了一个新的用户,ID为4 ,名字为James ,姓氏为Max 。然后,它断言响应的内容类型是JSON,状态代码是201 ,是一个创建的资源。
接下来创建一个测试来验证该测试可以获取创建的用户。
def test_fetch_users(client):
response = client.get("/users")
assert response.headers["Content-Type"] == "application/json"
assert response.status_code == 200
这个测试验证端点返回一个JSON响应,状态代码是200 ,表示请求成功。这两个测试只是一个开始;在在根目录下的文件tests/test_app.py ,还有更多的测试。通过从命令行运行pytest 来执行你的测试。

测试的通过验证了从Python数据结构中创建的API端点的行为与使用实际数据库的API端点的行为是一样的。
现在你的测试在本地通过了,把它们与你的持续集成环境整合起来,以确保部署到GitHub仓库的变化不会破坏应用程序。在本教程的这一部分,我们将使用CircleCI作为CI环境。
与CircleCI集成
要将CircleCI配置添加到您的项目中,请在您的项目文件夹的根部创建一个新的目录,名为.circleci 。在该目录中,创建一个名为config.yml 的文件。将此配置添加到.circleci/config.yml 文件中。
version: 2.1
orbs:
python: circleci/python@1.5.0
jobs:
build-and-test:
docker:
- image: cimg/python:3.10.2
steps:
- checkout
- python/install-packages:
pkg-manager: pip
- run:
name: Run tests
command: pytest
workflows:
sample:
jobs:
- build-and-test
这个CircleCI配置是一个简单的例子,说明如何配置CircleCI来运行你的测试。它指定你正在使用一个Python Docker镜像,然后使用pip 包管理器安装Python包,并使用pytest 命令运行你的测试。
设置CircleCI
现在你在远程main GitHub分支上有了代码,你可以设置CircleCI来运行你的测试。进入CircleCI仪表板,选择项目标签。在列表中找到你的存储库。对于本教程,它是python-api-with-datastructures 仓库。

选择设置项目的选项。因为你已经把你的CircleCI配置推送到了远程仓库,你可以直接输入包含配置的分支名称,然后点击设置项目。

坐下来,看着你的测试在CircleCI中执行。

你的测试成功通过了,这只意味着一件事:是时候庆祝一下了!
总结
通过跟随本教程的学习,你已经对Python数据结构有了扎实的了解,为什么需要它们,特别是如何在Python中使用列表和字典数据结构。你还学会了只使用数据结构来编写端点。你为你的API端点写了测试,以避免破坏现有的变化。你学会了如何集成CircleCI,并观察了CircleCI在CI平台上执行你的测试。