在类属性列表中的列表属性中查找匹配项

时间:2011-11-10 20:46:36

标签: python generator list-comprehension

我有一个类'Foo',它有一个名字(字符串)和一组数据(一个整数列表)。我需要能够在Foo列表中找到'test'任何字符串/列表组合,以找到任何匹配项。像这样:

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 个答案:

答案 0 :(得分:4)

您似乎可以重新构建代码中的许多内容,以使其更好地工作。

首先,我不是将数据作为列表,而是考虑一个集合。这样您就可以使用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 :(得分:1)

您可以使用集而不是列表:

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

答案 2 :(得分:0)

这基本上有效,但看起来很傻。
不傻,只是未经实验。

我希望有一种方法可以使用列表理解生成器来使它更漂亮。 (...)我怀疑这是更好的方式
当然,你的直觉很好。

一种改进的简单方法:

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,但元组是更轻的数据结构

修改

在评论中考虑了 gddc 的评论后,我将字典 lifoos 替换为字典 dicfoos :后者避免了查找需要精确名称的实例,具有此精确名称的itel给出了此类实例的列表