使用可选参数从 Python 中的字典列表中分离出来
Separating from a List of Dictionaries in Python with optional parameters
我正在尝试匹配一长串 Python 词典。我正在寻找的是根据字典键的值将此列表中的字典附加到新列表中。我所拥有的一个例子是:
1000 多个词典的列表结构如下:
{'regions': ['north','south'],
'age':35,
'name':'john',
'cars':['ford','kia']}
我想使用几乎所有键对该列表进行排序和匹配,并将匹配的键附加到新列表。有时我可能只使用 age
进行搜索,而其他时候我将使用 regions
和 name
进行搜索,一直到使用 age
、name
, regions
, & cars
因为要搜索的所有参数都是可选的。
我目前使用for循环对其进行排序,但随着我添加越来越多的可选参数,它变得越来越慢和复杂。有没有更简单的方法来陪伴我正在做的事情?
用户发送的示例是:
regions: north
age: 10
它会 return 一个包含 north 作为 region
和 10 作为age
我编写了代码来执行此操作
test = [
{
'regions': ['south'],
'age':35,
'name':'john',
'cars':['ford']
},
{
'regions': ['north'],
'age':15,
'name':'michael',
'cars':['kia']
},
{
'regions': ['north','south'],
'age':20,
'name':'terry',
'cars':['ford','kia']
},
{
'regions': ['East','south'],
'age':35,
'name':'user',
'cars':['other','kia']
},
{
'regions': ['East','south'],
'age':75,
'name':'john',
'cars':['other']
}
]
def Finder(inputs: list, regions: list = None, age: int = None, name: str = None, cars: list = None) -> list:
output = []
for input in inputs:
valid = True
if regions != None and valid: valid = all(i in input["regions"] for i in regions)
if age != None and valid: valid = input["age"] == age
if name != None and valid: valid = input["name"] == name
if cars != None and valid: valid = all(i in input["cars"] for i in cars)
if valid:
output.append(input)
return output
print(Finder(test))
print(Finder(test, age = 25))
print(Finder(test, regions = ["East"]))
print(Finder(test, cars = ["ford","kia"]))
print(Finder(test, name = "john", regions = ["south"]))
这个函数只是检查所有的参数和检查输入是否有效,他把所有有效的输入放在一个输出列表中
我认为这个非常开放,特别是因为您建议您希望它在添加更多键等时可扩展,并且没有真正讨论您的操作要求。但这里有一些想法:
第三方模块
这些词典是否会嵌套更多?或者它会一直是 'key -> value' 还是 'key -> [list, of, values]'?
如果你能接受一个大块的依赖,你可能会考虑像 pandas
这样的东西,我们通常认为它代表 tables,但它肯定可以在某种程度上管理嵌套。
例如:
from functools import partial
from pandas import DataFrame
from typing import Dict
def matcher(comparator, target=None) -> bool:
"""
matcher
Checks whether a value matches or contains a target
(won't match if the target is a substring of the value)
"""
if target == comparator: # simple case, return True immediately
return True
if isinstance(comparator, str):
return False # doesn't match exactly and string => no match
try: # handle looking in collections
return target in comparator
except TypeError: # if it fails, you know there's no match
return False
def search(data: DataFrame, query: Dict) -> DataFrame:
"""
search
Pass in a DataFrame and a query in the form of a dictionary
of keys and values to match, for example:
{"age": 42, "regions": "north", "cars": "ford"}
Returns a matching subset of the data
"""
# each element of the resulting list is a boolean series
# corresponding to a dictionary key
masks = [
data[key].map(partial(matcher, target=value)) for key, value in query.items()
]
# collapse the masks down to a single boolean series indicating
# whether ALL conditions are met for each record
mask = pd.concat(masks, axis="columns").all(axis="columns")
return data.loc[mask]
if __name__ == "__main__":
data = DataFrame(your_big_list)
query = {"age": 35, "regions": "north", "cars": "ford"}
results = search(data, query)
list_results = results.to_dict(orient="records")
此处 list_results
会将过滤后的数据恢复为原始格式,如果这对您很重要的话。
我发现matcher
这个功能要出奇的复杂,我一直在想edge-cases(比如:我们需要支持在集合中搜索,但是in
也可以找到子字符串,这不是我们想要的......当然,除非它是!)。
但至少所有逻辑都被隔离在那里。您可以为其编写一系列单元测试,如果您将来扩展架构,则可以相应地更改函数并检查测试是否仍然通过。
search
函数纯粹是为了让 pandas
做你想用 matcher
做的事。
match case
在 Python 3.10 中,新的 match case
语句可能允许您非常干净地封装匹配逻辑。
性能
这里的一个非常基本的问题是,如果您关心性能(而且我觉得这对您来说是次要的可维护性)那么
- 数据越大,速度越慢
- python已经不算快了,一般来说
您可以通过为您的数据建立某种索引来改进。然而,归根结底,使用专业工具总是会更可靠。这将是 某种 数据库。
具体细节将取决于您的要求。例如这些数据会变得非常非结构化吗?是否有任何字段将成为需要在 Elasticsearch/Solr 之类的内容中正确索引的文本?
您可以在短期内使用 Python 实施的真正 light-touch 解决方案是
- 将该数据放入 SQLite
- 搜索靠SQL
我建议 SQLite 因为它运行 out-of-the-box 并且只在一个本地文件中:
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydb.sql")
# ... that's it, we can now connect to a SQLite DB/the 'mydb.sql' file that will be created
...但缺点是不支持array-like数据。您的选择是:
- 改用 postgreSQL 并利用 运行 火力更大的数据库
- 标准化这些数据
我认为选项 2 不会太难。类似于:
REGIONS
id | name
----------
1 | north
2 | south
3 | east
4 | west
CUSTOMERS
id | age | name
---------------
...
REGION_LINKS
customer_id | region_id
-----------------------
1 | 1
1 | 2
我把主要数据称为table'customers'但是你没有提到这些数据真正代表什么,所以更多的是举例。
然后可以使用 sqlalchemy
的 ORM 功能构建和执行您的 SQL 查询。
我正在尝试匹配一长串 Python 词典。我正在寻找的是根据字典键的值将此列表中的字典附加到新列表中。我所拥有的一个例子是:
1000 多个词典的列表结构如下:
{'regions': ['north','south'],
'age':35,
'name':'john',
'cars':['ford','kia']}
我想使用几乎所有键对该列表进行排序和匹配,并将匹配的键附加到新列表。有时我可能只使用 age
进行搜索,而其他时候我将使用 regions
和 name
进行搜索,一直到使用 age
、name
, regions
, & cars
因为要搜索的所有参数都是可选的。
我目前使用for循环对其进行排序,但随着我添加越来越多的可选参数,它变得越来越慢和复杂。有没有更简单的方法来陪伴我正在做的事情?
用户发送的示例是:
regions: north
age: 10
它会 return 一个包含 north 作为 region
和 10 作为age
我编写了代码来执行此操作
test = [
{
'regions': ['south'],
'age':35,
'name':'john',
'cars':['ford']
},
{
'regions': ['north'],
'age':15,
'name':'michael',
'cars':['kia']
},
{
'regions': ['north','south'],
'age':20,
'name':'terry',
'cars':['ford','kia']
},
{
'regions': ['East','south'],
'age':35,
'name':'user',
'cars':['other','kia']
},
{
'regions': ['East','south'],
'age':75,
'name':'john',
'cars':['other']
}
]
def Finder(inputs: list, regions: list = None, age: int = None, name: str = None, cars: list = None) -> list:
output = []
for input in inputs:
valid = True
if regions != None and valid: valid = all(i in input["regions"] for i in regions)
if age != None and valid: valid = input["age"] == age
if name != None and valid: valid = input["name"] == name
if cars != None and valid: valid = all(i in input["cars"] for i in cars)
if valid:
output.append(input)
return output
print(Finder(test))
print(Finder(test, age = 25))
print(Finder(test, regions = ["East"]))
print(Finder(test, cars = ["ford","kia"]))
print(Finder(test, name = "john", regions = ["south"]))
这个函数只是检查所有的参数和检查输入是否有效,他把所有有效的输入放在一个输出列表中
我认为这个非常开放,特别是因为您建议您希望它在添加更多键等时可扩展,并且没有真正讨论您的操作要求。但这里有一些想法:
第三方模块
这些词典是否会嵌套更多?或者它会一直是 'key -> value' 还是 'key -> [list, of, values]'?
如果你能接受一个大块的依赖,你可能会考虑像 pandas
这样的东西,我们通常认为它代表 tables,但它肯定可以在某种程度上管理嵌套。
例如:
from functools import partial
from pandas import DataFrame
from typing import Dict
def matcher(comparator, target=None) -> bool:
"""
matcher
Checks whether a value matches or contains a target
(won't match if the target is a substring of the value)
"""
if target == comparator: # simple case, return True immediately
return True
if isinstance(comparator, str):
return False # doesn't match exactly and string => no match
try: # handle looking in collections
return target in comparator
except TypeError: # if it fails, you know there's no match
return False
def search(data: DataFrame, query: Dict) -> DataFrame:
"""
search
Pass in a DataFrame and a query in the form of a dictionary
of keys and values to match, for example:
{"age": 42, "regions": "north", "cars": "ford"}
Returns a matching subset of the data
"""
# each element of the resulting list is a boolean series
# corresponding to a dictionary key
masks = [
data[key].map(partial(matcher, target=value)) for key, value in query.items()
]
# collapse the masks down to a single boolean series indicating
# whether ALL conditions are met for each record
mask = pd.concat(masks, axis="columns").all(axis="columns")
return data.loc[mask]
if __name__ == "__main__":
data = DataFrame(your_big_list)
query = {"age": 35, "regions": "north", "cars": "ford"}
results = search(data, query)
list_results = results.to_dict(orient="records")
此处 list_results
会将过滤后的数据恢复为原始格式,如果这对您很重要的话。
我发现matcher
这个功能要出奇的复杂,我一直在想edge-cases(比如:我们需要支持在集合中搜索,但是in
也可以找到子字符串,这不是我们想要的......当然,除非它是!)。
但至少所有逻辑都被隔离在那里。您可以为其编写一系列单元测试,如果您将来扩展架构,则可以相应地更改函数并检查测试是否仍然通过。
search
函数纯粹是为了让 pandas
做你想用 matcher
做的事。
match case
在 Python 3.10 中,新的 match case
语句可能允许您非常干净地封装匹配逻辑。
性能
这里的一个非常基本的问题是,如果您关心性能(而且我觉得这对您来说是次要的可维护性)那么
- 数据越大,速度越慢
- python已经不算快了,一般来说
您可以通过为您的数据建立某种索引来改进。然而,归根结底,使用专业工具总是会更可靠。这将是 某种 数据库。
具体细节将取决于您的要求。例如这些数据会变得非常非结构化吗?是否有任何字段将成为需要在 Elasticsearch/Solr 之类的内容中正确索引的文本?
您可以在短期内使用 Python 实施的真正 light-touch 解决方案是
- 将该数据放入 SQLite
- 搜索靠SQL
我建议 SQLite 因为它运行 out-of-the-box 并且只在一个本地文件中:
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydb.sql")
# ... that's it, we can now connect to a SQLite DB/the 'mydb.sql' file that will be created
...但缺点是不支持array-like数据。您的选择是:
- 改用 postgreSQL 并利用 运行 火力更大的数据库
- 标准化这些数据
我认为选项 2 不会太难。类似于:
REGIONS
id | name
----------
1 | north
2 | south
3 | east
4 | west
CUSTOMERS
id | age | name
---------------
...
REGION_LINKS
customer_id | region_id
-----------------------
1 | 1
1 | 2
我把主要数据称为table'customers'但是你没有提到这些数据真正代表什么,所以更多的是举例。
然后可以使用 sqlalchemy
的 ORM 功能构建和执行您的 SQL 查询。