Python 动态列表理解

Python 动态列表理解,python,list-comprehension,Python,List Comprehension,我想知道python是否能够通过使用多个可选条件创建列表 让我们举个例子。考虑以下对象(部分描述): 但是由于用户可以选择他想要的,我不知道使用什么标准。我当然可以将其构建为完全成熟的功能: 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

我想知道python是否能够通过使用多个可选条件创建列表

让我们举个例子。考虑以下对象(部分描述):

但是由于用户可以选择他想要的,我不知道使用什么标准。我当然可以将其构建为完全成熟的功能:

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
l=world
如果名称搜索:
l=[如果重新搜索(person.name,nameSearch),则l中的人员对应人员]
如果性别研究:
l=[如果性别==性别搜索,则l中的人对人]
返回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中的第一类公民,您可以编写一组匹配器函数,将它们(动态地)放在一个列表中,并在单个列表中与它们进行匹配

谓词
成为类型为
Person->bool
的单参数函数列表

然后简单地做:

[ pers for pers in world if all([f(pers) for f in predicates]) ]
进一步探索功能思路,您可以通过创建返回匹配函数的函数来创建“动态匹配函数”:

def age_matcher(age):
    return lambda p: p.age > age
可以将
age\u matcher(someAge)
添加到
谓词
数组中

旁注


对于这些类似于“数据库搜索”的任务,您可能希望真正应该查看库,例如,您可以在其中进行类似于SQL的查询。您可能正在重新发明一种相当复杂的轮子。

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

由于函数是Python中的头等公民,因此您可以编写一组匹配器函数,将它们(动态地)放入在一个列表中,并在一个单一的列表理解中与他们匹配。熊猫是一个非常好的建议,看看熊猫库似乎绝对有趣。lambda的想法也很好,我只是不确定它是否可以应用于年龄条件。既然你有匹配的谓词,比如说,
==
,它接受一个人作为参数,但是你如何传递相等的数字参数呢?我知道你可以做一个
def isMasculine(person):return person.gender='m'
函数,这肯定会起作用。Thanks@M例如,您可以创建一个函数,获取年龄阈值并返回一个与该阈值匹配的年龄函数。我将它添加到我的答案中。最好使用理解而不是映射和lambda:
all(f(pers)表示谓词中的f)
@YannVernier可能更好,因为我无法使它与映射一起工作。创建一个生成器,然后在其上使用
列表
方法有什么好处吗,不是直接使用列表理解,而是实现用于打印的生成器。我不是python专家,在这种情况下,您当然可以用列表替换生成器表达式。我认为无论何时处理较长的列表,都应该首选生成器表达式。在Python3中,列表理解
[…]
只是为列表类型构造函数提供生成器表达式的语法糖分。我想我应该停止写注释,而是写答案;-)@DCS我认为点共享将是一个有趣的补充,因此:)self.l在课堂上没有定义,这意味着什么?
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)]