Поиск совпадений в свойстве списка, в списке экземпляров класса - PullRequest
2 голосов
/ 11 ноября 2011

У меня есть класс 'Foo', который имеет имя (строку) и набор данных (список целых чисел).Мне нужно быть в состоянии найти «проверить» любую комбинацию строка / список со списком Foo, чтобы найти любые совпадения.Вот так:

class Foo:
    def __init__(self, name, data):
        self.name = str(name)
        self.data = list(data)


foo1 = Foo('abc', [1, 2, 3])
foo2 = Foo('def', [4, 5, 6])
foo3 = Foo('ghi', [7, 8, 9])

my_list = [foo1, foo2, foo3]

def test(name, data):
    results = []
    for foo in my_list:
        if foo.name == name:
            for number in data:
                if number in foo.data:
                    results.append(number)
    return name, results

print test('def', [2, 3, 4, 5])

вернет

('def', [4, 5])

, а ...

print test('gah', [1, 2, 3])

вернет

('gah', [])

Это в основном работает, но это выглядит глупо.Я надеялся, что есть способ использовать списки или генераторы, чтобы сделать их красивее.Мне не обязательно пытаться свести все к одному выражению в одну строку, так как я ожидаю, что это будет почти невозможно прочитать, но я подозреваю, что есть лучший способ сделать это.

Ответы [ 3 ]

4 голосов
/ 11 ноября 2011

Кажется, что вы можете реструктурировать несколько вещей в своем коде, чтобы он работал немного лучше.

Во-первых, вместо того, чтобы данные были списком, я бы рассмотрел множество. Это позволит вам использовать data.intersection(otherdata) для перекрытия.

Далее, вместо списка экземпляров Foo, возможно, словарь с ключевыми именами? Это позволило бы вам индексировать его по имени вашего теста, а не перебирать список экземпляров, чтобы найти подходящий.

class Foo:
    def __init__(self, name, data):
        self.name = str(name)
        self.data = set(data)


foo1 = Foo('abc', [1, 2, 3])
foo2 = Foo('def', [4, 5, 6])
foo3 = Foo('ghi', [7, 8, 9])

my_lookup = dict((f.name, f) for f in [foo1, foo2, foo3])

def test(name, data):
    if name in my_lookup:
        return name, my_lookup[name].data.intersection(data)
    return name, []

Я понял, что если вы проверили имя, которого у вас не было, вы получите KeyError, поэтому я настроил его для соответствующей обработки.

1 голос
/ 11 ноября 2011

Вы можете использовать наборы вместо списков:

from itertools import chain

def test(name, data):
    data = frozenset(data)
    return name, list(chain.from_iterable(data & set(foo.data)
                                          for foo in my_list
                                          if foo.name == name))

Посмотреть, как он работает онлайн: ideone

0 голосов
/ 12 ноября 2011

Это в принципе работает, но выглядит глупо.
Не глупо, просто неопытно.

Я надеялся, что есть способ использовать понимание списка или генераторов , чтобы сделать его красивее.(...) Я подозреваю есть лучший способ сделать это.
Безусловно, ваша интуиция была хороша.

Простой способулучшение:

class Foo:
    dicfoos = {}
    def __init__(self, name, data):
        self.name = str(name)
        self.data = list(data)
        self.__class__.dicfoos.setdefault(self.name,[]).append(self) 

foo1 = Foo('abc', [1, 2, 3])
foo2 = Foo('def', [4, 5, 6])
foo3 = Foo('ghi', [7, 8, 9])
foo4 = Foo('def', [10, 11, 12])

def test(klass,the_name, wanted_data):
    return (the_name,
            tuple( x for foo in klass.dicfoos.get(the_name,())
                   for x in foo.data if x in wanted_data ) )

print test(Foo,'zzz', [2, 3, 4, 5, 11])
print test(Foo,'def', [2, 3, 4, 5, 11])
print test(Foo,'abc', [2, 3, 4, 5, 11])

результат

('zzz', ())
('def', (4, 5, 11))
('abc', (2, 3))

Немного более сложный способ сделать:

class Foo:
    dicfoos = {}
    def __init__(self, name, data):
        self.name = str(name)
        self.data = list(data)
        self.__class__.dicfoos.setdefault(self.name,[]).append(self)
    def sift(self,daataa):
        for n in self.data:
            if n in daataa:  yield n

foo1 = Foo('abc', [1, 2, 3])
foo2 = Foo('def', [4, 5, 6])
foo3 = Foo('ghi', [7, 8, 9])
foo4 = Foo('def', [10, 11, 12])


def test(klass,the_name,wanted_data):
    return (the_name,
            tuple( x for foo in klass.dicfoos.get(the_name,())
                   for x in foo.sift(wanted_data) ) )

print test(Foo,'zzz', [2, 3, 4, 5, 11])
print test(Foo,'def', [2, 3, 4, 5, 11])
print test(Foo,'abc', [2, 3, 4, 5, 11])

Вы можете заменить имя tuple на listесли это действительно необходимо, но кортеж - это более легкая структура данных

Edit

Принимая во внимание замечание gddc в комментарии, я заменил список lifoos со словарем dicfoos : последний избегает поиска, когда нужны экземпляры с точным именем, itel с этим точным именем дает список таких экземпляров

...