如何在Python中实现高效的过滤逻辑?

7 投票
2 回答
1698 浏览
提问于 2025-04-18 07:35

我正在尝试创建一个程序,用来存储以下信息:

  1. 水果名称
  2. 水果类型
  3. 水果颜色
  4. 水果大小

当用户需要时,可以把这些信息展示给他们。用户会有一些预先定义的选项可以选择,类似于下面这个:

在这里输入图片描述

我的数据库表格大概是这样的:

在这里输入图片描述

现在,我想实现一个过滤功能,让用户可以选择:

  • 水果类型
  • 水果颜色
  • 水果大小

然后程序会返回所有符合这些条件的水果名称。不过,现在我还有一个额外的选项,“全部”。

在这里输入图片描述

假设我已经查询出了所有水果的数据,并把它们存储在一个字典里,像这样:

myfruits = { 'apple':('fleshy','red','medium'),
            'orange':('fleshy','orange','medium'),
             'peanut':('dry','red','small'),...}

我该如何获取用户选择的三种属性对应的水果名称列表呢?(比如,如果用户选择了“肉质”类型,“全部”颜色和“全部”大小,程序应该返回 ['apple','orange']。)

我考虑过使用 if 语句,但随着属性数量的增加,我需要写很多行 ifelse,我觉得这样不太可行。

我使用的是 Python 2.7,搭配 PyQt 4 和 SQLite 3 数据库,运行在 Windows XP SP3 32位系统上。

2 个回答

10

在这里,我会使用SQLAlchemy来处理数据库的部分,你可以把它当作对象关系映射(ORM)来用,或者只是用它来生成SQL语句

这样你就可以动态生成过滤条件;你可以遍历这些过滤条件,如果某个特定的条件没有被设置为“全部”,就可以根据这个条件来限制查询。

使用SQLAlchemy仅仅生成SQL语句的代码大概是这样的:

from sqlalchemy.sql import select

fruit_query = select([fruits])
for filtername in ui_filters:
    filtervalue = obtain_filter_value_for(filtername)
    if filtervalue != 'All':
        fruit_query = fruit_query.where(filtername == filtervalue)

另一种方法是手动生成WHERE条件:

query = 'SELECT * FROM fruits'
params = []
whereclauses = []

for filtername in ui_filters:
    filtervalue = obtain_filter_value_for(filtername)
    if filtervalue != 'All':
        whereclauses.append('{} = ?'.format(filtername))
        params.append(filtervalue)

if whereclauses:
    query = '{} WHERE {}'.format(query, ' AND '.join(whereclauses))

cursor.execute(query, params)

但要注意,SQLAlchemy的表达式引擎要灵活得多,而且出错的可能性也小得多。很快你就会想要添加更复杂的过滤条件(比如范围搜索、文本搜索、将多个条件用“或”组合起来),而SQLAlchemy可以让生成这些条件变得简单得多,真的。

3

如果你已经把所有数据都查询出来了,一个简单的方法就是直接使用 filter 函数:

def predicate(fruit_type, fruit_color, fruit_size):
    def _predicate(fruit):
        if not fruit_type == 'All' and not fruit_type == fruit[1][0]:
            return False
        if not fruit_color == 'All' and not fruit_color == fruit[1][1]:
            return False
        if not fruit_size == 'All' and not fruit_size == fruit[1][2]:
            return False
        return True
    return _predicate

query_type = 'All'
query_color = 'All'
query_size = 'All'
myfruits = {}
my_filtered_fruit = list(filter(predicate(query_type, query_color, query_size), myfruits.items()))

另一种方法是定义一个叫 Predicate 的对象,它包含一个视图(也就是过滤器的名字)和与之相关的过滤函数:

class Predicate:
    def __init__(self, predicate, view):
        self.predicate = predicate
        self.view = view

# Creation of the predicates :
all_color = Predicate(lambda fruit: True, 'All colors')
red_color = Predicate(lambda fruit: fruit[2] == 'red')
# ...

# Then you have to generate your select form. I don't remember exactly the PyQt4 doc but it's not the harder part.

predicates = getAllSelected() # I guess you know how to get this kind of function

myfruits = {}
my_filtered_fruits = myfruits.items()
for pred in predicates:
    my_filtered_fruit = filter(lambda x: pred(x[1]), my_filtered_fruit)
my_filtered_fruit = list(my_filtered_fruit)

撰写回答