2015-01-15 3 views
2

Я хотел бы знать, может ли 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 выходит за рамки вопроса), например имя (на основе regex), id, пол и возраст (с равным, не равным и больше или меньше). Ни один из критериев поиска не является обязательным (мы можем предположить, что это None, я думаю), и тип не имеет значения для этого вопроса.

Как я могу отфильтровать список Person в умном python-way?

Если бы я знал критерии, которые я мог бы сделать понимание:

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 

Но я чувствую питон бы способ сделать это более правильно.

+2

Поскольку функции являются гражданами первого класса на Python, вы можете написать набор функций-совпадений, поместить их (динамически) в список и сопоставить с ними в одном понимании списка. – DCS

ответ

1

Разрабатывая мой комментарий выше:

Так как функции являются первыми гражданами класса в Python, вы могли бы написать кучу функций Сличитель, положить их (динамически) в список и сопоставление с ними в одном понимании списка.

Позвольте predicates быть списком однопараметрических функций типа 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_matcher(someAge) могут быть добавлены к вашему массиву predicates.

Примечание стороны

Для этих «баз данных поиска» -подобных задачи, вы , вероятно, захотите действительно должен смотреть в библиотеках, как Pandas, где вы можете сделать запросы похожи на SQL. Возможно, вы изобретаете довольно сложный тип колеса.

+1

Pandas - действительно хорошее предложение здесь, посмотрите http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.query.html – filmor

+0

добавил вашу ссылку на мой ответ, спасибо – DCS

+0

Panda lib кажется определенно интересным. Идея лямбда тоже хороша, я просто не уверен, что ее можно применить, например, к возрастному состоянию. Поскольку у вас есть соответствующие предикаты, давайте скажем '==', который принимает аргумент Person как, но как вы передаете аргумент number для равенства? Я понимаю, что вы можете, однако, выполнить функцию def isMasculine (person): return person.gender == 'm'', и это определенно сработает. Спасибо –

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") 
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 Чтобы выбрать всех людей, которые соответствуют любых указанных фильтров.

+0

есть ли какая-либо польза от создания генератора, а затем с помощью метода 'list' на нем вместо прямого использования списка? –

+0

'list' просто реализует генератор для печати. Я не эксперт по python, в этом случае вы, безусловно, замените выражение генератора на понимание списка. Я думаю, что выражение генератора должно быть предпочтительным при работе с более длинными списками. В Python 3 понимание списка '[...]' является синтаксическим сахаром для выражения генератора для конструктора типов списков. – Matt

+0

Я должен прекратить писать комментарии и писать ответы вместо этого, я думаю ;-) – DCS

Смежные вопросы