动态列表理解
Dynamic list comprehension
我想知道 python 是否能够使用多个可选条件通过理解来创建列表。
我们举个例子。考虑以下对象(部分描述):
class Person():
def __init__(self):
self.id = <next id of some kind>
self.name = 'default name'
self.gender = 'm'
self.age = 20
<...>
假设我创建了一个包含所有 Person
的列表到 world
。然后我想创建一个 GUI,它允许我根据搜索条件浏览集合(GUI 概念不在问题范围之内),例如名称(基于正则表达式)、id、性别和年龄(等于,不等于以及大于或小于)。 None 的搜索条件是强制性的(我们可以假设它是 None
我猜)并且类型对于这个问题并不重要。
如何以一种巧妙的 python 方式过滤 Person
的列表?
如果我知道标准,我可以理解 :
l = [person for person in world if re.search(person.name, '.*Smith') and person.gender = 'm' and person.age < 20]
但是由于用户可以选择他想要的内容,所以我不知道使用什么标准。我当然可以将其构建为完全成熟的函数:
l = world
if nameSearch:
l = [person for person in l if re.search(person.name, nameSearch)]
if genderSearch:
l = [person for person in l if gender == genderSearch]
<...>
return l
但我觉得 python 会有更合适的方法。
这个怎么样?
def search(self, condition):
return filter(condition, self.l)
def search_re(self, **kwargs):
filters = []
for key, value in kwargs.items():
if isinstance(value, str):
value = re.compile(value)
filters.append(lambda x: re.search(getattr(x, key), value))
elif callable(value):
filters.append(lambda x: value(getattr(x, key)))
else:
filters.append(lambda x: getattr(x, key) == value)
def condition(person):
return all(
f(person) for f in filters
)
return self.search(condition)
用法:
persons.search(lambda x: x.name == "bla")
persons.search_re(name=".*Smith", gender="male")
在上面阐述我的评论:
由于函数是 Python 中的第一个 class 公民,您可以编写一堆匹配器函数,将它们(动态地)放在一个列表中,并在单个列表理解中与它们匹配。
设 predicates
为 Person -> bool
.
类型的单参数函数列表
然后简单地做:
[ pers for pers in world if all([f(pers) for f in predicates]) ]
进一步探索函数式的思路,可以通过创建返回匹配函数的函数来创建"dynamic matching functions":
def age_matcher(age):
return lambda p: p.age > age
可以将 age_matcher(someAge)
添加到您的 predicates
数组。
旁注
对于这些类似于 "database-search" 的任务,您 可能想要 真的应该看看像 Pandas 这样的库,您可以在其中进行类似于SQL。您可能正在重新发明一种相当复杂的轮子。
根据 DCS 的评论,这里是一个如何将函数用作过滤器的示例。过滤器只是一个 returns 布尔值的函数(给定 Person
的实例)。为了加快处理速度,我建议您查看 pandas
,这是数据 filtering/sorting/munging 的一个很好的选择,但这可能会让您开始使用一个简单的解决方案。留给您的唯一任务是根据用户的输入创建过滤器。
from random import random
class Person():
def __init__(self, id):
self.id = id
self.name = 'Name{}'.format(id)
self.gender = 'm' if random() > 0.5 else 'f'
self.age = int(random() * 10) + 10
def __repr__(self):
return 'Person-{} ({}, {}. {})'.format(self.id,
self.name,
self.gender,
self.age)
设置一些测试数据:
people = [Person(id) for id in range(10)]
[Person-0 (Name0, f. 15),
Person-1 (Name1, f. 14),
Person-2 (Name2, f. 12),
Person-3 (Name3, f. 18),
Person-4 (Name4, m. 12),
Person-5 (Name5, f. 18),
Person-6 (Name6, f. 15),
Person-7 (Name7, f. 15),
Person-8 (Name8, f. 10),
Person-9 (Name9, m. 16)]
输出:
def by_age(age):
return lambda person: person.age == age
def by_name(name):
return lambda person: re.search(person.name, name)
def by_gender(gender):
return lambda person: person.gender == gender
filters = (by_age(15),
by_gender('f'))
filtered_people = (p for p in people if all([f(p) for f in filters]))
list(filtered_people)
这为我们提供了以下经过筛选的人员列表:
[Person-0 (Name0, f. 15), Person-6 (Name6, f. 15), Person-7 (Name7, f. 15)]
您甚至可以将谓词 all
更改为 any
,以便 select 所有匹配指定过滤器 any 的人。
我想知道 python 是否能够使用多个可选条件通过理解来创建列表。
我们举个例子。考虑以下对象(部分描述):
class Person():
def __init__(self):
self.id = <next id of some kind>
self.name = 'default name'
self.gender = 'm'
self.age = 20
<...>
假设我创建了一个包含所有 Person
的列表到 world
。然后我想创建一个 GUI,它允许我根据搜索条件浏览集合(GUI 概念不在问题范围之内),例如名称(基于正则表达式)、id、性别和年龄(等于,不等于以及大于或小于)。 None 的搜索条件是强制性的(我们可以假设它是 None
我猜)并且类型对于这个问题并不重要。
如何以一种巧妙的 python 方式过滤 Person
的列表?
如果我知道标准,我可以理解 :
l = [person for person in world if re.search(person.name, '.*Smith') and person.gender = 'm' and person.age < 20]
但是由于用户可以选择他想要的内容,所以我不知道使用什么标准。我当然可以将其构建为完全成熟的函数:
l = world
if nameSearch:
l = [person for person in l if re.search(person.name, nameSearch)]
if genderSearch:
l = [person for person in l if gender == genderSearch]
<...>
return l
但我觉得 python 会有更合适的方法。
这个怎么样?
def search(self, condition):
return filter(condition, self.l)
def search_re(self, **kwargs):
filters = []
for key, value in kwargs.items():
if isinstance(value, str):
value = re.compile(value)
filters.append(lambda x: re.search(getattr(x, key), value))
elif callable(value):
filters.append(lambda x: value(getattr(x, key)))
else:
filters.append(lambda x: getattr(x, key) == value)
def condition(person):
return all(
f(person) for f in filters
)
return self.search(condition)
用法:
persons.search(lambda x: x.name == "bla")
persons.search_re(name=".*Smith", gender="male")
在上面阐述我的评论:
由于函数是 Python 中的第一个 class 公民,您可以编写一堆匹配器函数,将它们(动态地)放在一个列表中,并在单个列表理解中与它们匹配。
设 predicates
为 Person -> bool
.
然后简单地做:
[ pers for pers in world if all([f(pers) for f in predicates]) ]
进一步探索函数式的思路,可以通过创建返回匹配函数的函数来创建"dynamic matching functions":
def age_matcher(age):
return lambda p: p.age > age
可以将 age_matcher(someAge)
添加到您的 predicates
数组。
旁注
对于这些类似于 "database-search" 的任务,您 可能想要 真的应该看看像 Pandas 这样的库,您可以在其中进行类似于SQL。您可能正在重新发明一种相当复杂的轮子。
根据 DCS 的评论,这里是一个如何将函数用作过滤器的示例。过滤器只是一个 returns 布尔值的函数(给定 Person
的实例)。为了加快处理速度,我建议您查看 pandas
,这是数据 filtering/sorting/munging 的一个很好的选择,但这可能会让您开始使用一个简单的解决方案。留给您的唯一任务是根据用户的输入创建过滤器。
from random import random
class Person():
def __init__(self, id):
self.id = id
self.name = 'Name{}'.format(id)
self.gender = 'm' if random() > 0.5 else 'f'
self.age = int(random() * 10) + 10
def __repr__(self):
return 'Person-{} ({}, {}. {})'.format(self.id,
self.name,
self.gender,
self.age)
设置一些测试数据:
people = [Person(id) for id in range(10)]
[Person-0 (Name0, f. 15),
Person-1 (Name1, f. 14),
Person-2 (Name2, f. 12),
Person-3 (Name3, f. 18),
Person-4 (Name4, m. 12),
Person-5 (Name5, f. 18),
Person-6 (Name6, f. 15),
Person-7 (Name7, f. 15),
Person-8 (Name8, f. 10),
Person-9 (Name9, m. 16)]
输出:
def by_age(age):
return lambda person: person.age == age
def by_name(name):
return lambda person: re.search(person.name, name)
def by_gender(gender):
return lambda person: person.gender == gender
filters = (by_age(15),
by_gender('f'))
filtered_people = (p for p in people if all([f(p) for f in filters]))
list(filtered_people)
这为我们提供了以下经过筛选的人员列表:
[Person-0 (Name0, f. 15), Person-6 (Name6, f. 15), Person-7 (Name7, f. 15)]
您甚至可以将谓词 all
更改为 any
,以便 select 所有匹配指定过滤器 any 的人。