动态列表理解

时间:2015-01-15 09:17:09

标签: python 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我猜)并且这个类型对于这个问题并不重要。

如何以巧妙的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可以更好地做到这一点。

3 个答案:

答案 0 :(得分:2)

阐述我的评论:

由于函数是Python中的一等公民,你可以编写一堆匹配函数,将它们(动态地)放在一个列表中,并在单个列表理解中与它们匹配。

predicates成为Person -> bool类型的单参数函数列表。

然后只需:

[ pers for pers in world if all([f(pers) for f in predicates]) ]

进一步探索思维的功能路线,可以创建动态匹配功能&#34;通过创建返回匹配函数的函数:

def age_matcher(age):
    return lambda p: p.age > age

可以将age_matcher(someAge)添加到您的predicates数组中。

旁注

对于这些&#34;数据库搜索&#34;类似的任务,你可能希望真的应该查看像Pandas这样的库,在那里你可以进行类似的查询SQL。你可能正在重新发明一种相当复杂的轮子。

答案 1 :(得分:2)

基于DCS的评论,这里是一个如何使用函数作为过滤器的示例。过滤器只是一个返回布尔值的函数(给定Person的实例)。为了加快处理速度,我建议你看看pandas,这是数据过滤/排序/修改的一个很好的选择,但这可能会让你开始使用一个简单的解决方案。留给您的唯一任务是根据用户的输入创建过滤器。

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,以便选择与指定过滤器任何匹配的所有人。

答案 2 :(得分:1)

这个怎么样?

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")