Multi Lookup Keys Dict

2024-10-10

难度: 2

时长: 30 min

问题

我有一个二维表格,经常需要用其中的一列的值去找同一行其它列的值,使用 Python 编程,有哪些做法?

答案 (doubao)

以下是一种使用 Python 实现的学生信息表接口设计方案,以便能够方便且高效地通过姓名或学号访问学生的所有成绩。

一、使用字典存储学生信息

可以使用两个字典分别以学号和姓名为键,存储学生的信息。这样可以快速地通过学号或姓名进行查找。

students = {}
students_by_name = {}

class Student:
    def __init__(self, student_id, name, scores):
        self.student_id = student_id
        self.name = name
        self.scores = scores

def add_student(student_id, name, scores):
    student = Student(student_id, name, scores)
    students[student_id] = student
    students_by_name[name] = student

def get_student_by_id(student_id):
    return students.get(student_id)

def get_student_by_name(name):
    return students_by_name.get(name)

使用示例:

add_student(1, 'Alice', {'math': 90, 'science': 85})
add_student(2, 'Bob', {'math': 80, 'science': 75})

student_by_id = get_student_by_id(1)
if student_by_id:
    print(f"Student with ID 1: {student_by_id.name}, Scores: {student_by_id.scores}")

student_by_name = get_student_by_name('Bob')
if student_by_name:
    print(f"Student with name Bob: ID {student_by_name.student_id}, Scores: {student_by_name.scores}")

二、使用数据库(如果数据量较大或需要持久化存储)

可以使用关系型数据库(如 SQLite)或非关系型数据库(如 MongoDB)来存储学生信息,并提供相应的查询接口。

以 SQLite 为例,可以使用sqlite3库来实现:

import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('''
CREATE TABLE IF NOT EXISTS students (
    student_id INTEGER PRIMARY KEY,
    name TEXT UNIQUE,
    math_score INTEGER,
    science_score INTEGER
)
''')

def add_student_db(student_id, name, math_score, science_score):
    cursor.execute("INSERT INTO students (student_id, name, math_score, science_score) VALUES (?,?,?,?)",
                   (student_id, name, math_score, science_score))
    conn.commit()

def get_student_by_id_db(student_id):
    cursor.execute("SELECT * FROM students WHERE student_id =?", (student_id,))
    return cursor.fetchone()

def get_student_by_name_db(name):
    cursor.execute("SELECT * FROM students WHERE name =?", (name,))
    return cursor.fetchone()

使用示例:

add_student_db(1, 'Alice', 90, 85)
add_student_db(2, 'Bob', 80, 75)

student_by_id = get_student_by_id_db(1)
if student_by_id:
    print(f"Student with ID 1: {student_by_id[1]}, Scores: Math - {student_by_id[2]}, Science - {student_by_id[3]}")

student_by_name = get_student_by_name_db('Bob')
if student_by_name:
    print(f"Student with name Bob: ID {student_by_name[0]}, Scores: Math - {student_by_name[2]}, Science - {student_by_name[3]}")

conn.close()

这样的接口设计可以方便地根据学号或姓名快速获取学生的所有成绩信息,并且在数据量较大时也能保持较好的性能。如果需要添加更多的课程成绩或其他字段,只需要在存储和查询的逻辑中进行相应的调整即可。

为什么多键查找很重要?

高效性问题

多键检索适合了实际应用时的各种需要,它和 dict 只有一个键是很不一样的。但对于少量几个键,如学号和姓名,可以定义相应的函数,来实现多键查找体验。

数据库也是这样做的吗?

豆包的问答: Python Dict Vs Database

数据一致性问题

doubao 的回答

和解法一相似但又不同的地方是,可以定义一个主键 id,然后定义从 name 到 id 的字典,需要用 name 查询时,就分2步完成,即先从 name 查 id,再从 id 查分数。这种方法相比前面的方法有何优缺点?

在解法一中,如果数据存在两份确实可能会面临数据不一致的风险,并且在修改数据时需要修改两处,这会带来以下一些问题:

一、可能导致的数据不一致情况

  1. 部分更新问题

    • 如果只修改了其中一处存储的数据而忘记修改另一处,就会导致数据不一致。例如,更新了以学号为键的字典中的成绩信息,却忘记更新以姓名为键的字典中的对应信息,那么后续通过姓名查询时就会得到错误的成绩数据。

  2. 并发修改风险

    • 在多线程或多进程环境下,同时对两份数据进行修改时,如果没有适当的同步机制,很容易出现数据不一致的情况。一个线程可能在另一个线程只更新了一处数据时进行了另一次修改,导致数据混乱。

二、解决方法和考虑因素

  1. 封装修改操作

    • 可以将对学生数据的修改操作封装在一个函数或方法中,确保在修改数据时同时更新两处存储。这样可以减少因人为疏忽而导致的数据不一致问题。

    • 例如:

      def update_student(student_id, name, new_scores):
          student = students[student_id]
          student.scores = new_scores
          students_by_name[name] = student
      
  2. 使用数据库事务(如果使用数据库存储学生信息)

    • 如果采用数据库来存储学生信息,可以利用数据库的事务机制来保证数据的一致性。在一个事务中进行数据的修改操作,要么全部成功提交,要么全部回滚,避免出现部分更新的情况。

  3. 数据同步策略

    • 考虑使用数据同步机制,例如在修改一处数据后触发一个事件或回调函数,自动更新另一处数据。这样可以降低手动更新两处数据的复杂性和出错的可能性。

  4. 性能影响

    • 同时更新两处数据可能会对性能产生一定的影响,特别是在频繁修改数据的情况下。需要权衡数据一致性和性能之间的关系,选择合适的解决方案。

总之,在解法一中需要特别注意数据的一致性问题,通过合理的设计和封装修改操作,可以降低数据不一致的风险。如果数据一致性要求非常高,可以考虑使用更严格的数据库事务或数据同步机制。