【问题标题】:What is a good way to narrow down a list of elements by their attribute or other conditions?按属性或其他条件缩小元素列表的好方法是什么?
【发布时间】:2013-02-17 01:51:08
【问题描述】:

我想通过属性、类型或其他条件来缩小列表中的元素列表。

elements.only_type(Flower).get_nearest_to(player) 之类的东西看起来比 min(filter(lambda i: isinstance(i, Flower), elements), lambda i: i.pos.distance_to(player.pos) 好得多。

就效率、简洁的代码和简单性而言,以下是一个好主意吗? 或者是否已经有一个好的方法、实现或设计模式?

class Selector(object):
    def __init__(self, selection):
        self.s3l3ct1on = selection

    def __getattr__(self, name):
        return type(self)(getattr(el, name) for el in self.s3l3ct1on)

    def __iter__(self):
        return iter(self.s3l3ct1on)

    def filter(self, function):
        return type(self)(filter(function, self.s3l3ct1on))

这就是它的使用方法:(A 只是一个具有两个属性的类:ab

>>> sel = [A(3, 4), A(0, 9), A('test', 3), A(4,22), A(3, 9)]
>>> Selector(sel)
<__main__.Selector object at 0x13b0a90>
>>> list(Selector(sel))
[<__main__.A object at 0x13b0fd0>,
 <__main__.A object at 0x13b0b50>,
 <__main__.A object at 0x13b0150>,
 <__main__.A object at 0x13b0710>,
 <__main__.A object at 0x13b06d0>]
>>> set(Selector(sel).a)
{0, 'test', 3, 4}
>>> list(Selector(sel).b)
[4, 9, 3, 22, 9]
>>> s = Selector(sel).b.filter(lambda i: i%2 == 0)
>>> list(s)
[4, 22]

【问题讨论】:

  • 顺便说一句,没有必要将实例属性命名为s3l3ct1on 而不是selection

标签: python optimization filtering


【解决方案1】:

您提到的每一种选择过程都可以用itertools 迭代器来定义。以下将其形式化,并且可以轻松扩展以支持更多种类。使用它的语法看起来非常易读。

虽然我没有进行任何性能测试,但我希望结果具有竞争力,因为所涉及的几乎所有开销都在构造方法中。唯一可能更快一点的事情是用等效的itertools迭代器函数替换它的用法——所以如果你希望做很多事情,它似乎是一个有效的便利工具这种处理方式。

import itertools

class Selector(object):
    def __init__(self, iterable, **kwargs):
        if not kwargs:
            self.iterator = iterable
        elif len(kwargs) > 1:
            raise ValueError('only one selector type keyword allowed')
        else:
            selector, target = kwargs.items()[0]

            if selector == 'by_attr':
                self.iterator = itertools.imap(lambda obj: getattr(obj, target), iterable)
            elif selector == 'by_type':
                self.iterator = itertools.ifilter(lambda obj: isinstance(obj, target), 
                                                  iterable)
            elif selector == 'by_func':
                self.iterator = itertools.ifilter(target, iterable)
            else:
                raise ValueError('unknown selector type keyword')

    def __iter__(self):
        return self.iterator


if __name__ == '__main__':
    from selector import Selector

    class A(object):
        def __init__(self, a, b):
            self.a, self.b = a, b

    class Flower(object):
        def __init__(self, name):
            self.name = name

    sel = [A(3, 4), A(0, 9), A('test', 3), A(4,22), A(3, 9)]
    print list(Selector(sel, by_attr='a'))

    sel = [42, Flower('Buttercup'), [1,2,3,5,8], A(20, 13), Flower('Rose')]
    print list(Selector(sel, by_type=Flower))

    sel = [4, 9, 3, 22, 9]
    print list(Selector(sel, by_func=lambda i: i%2 == 0))

【讨论】:

    【解决方案2】:

    为简单起见,您可以将类本身设为列表的子类。

    class Elements(list):
       def only_type(self, t):
           return Elements(i for i in self if isinstance(i, t))
    
       def get_nearest(self, who):
           return min(self, key=lambda x: x.pos.distance_to(who.pos))
    
    el = Elements([Flower(), Person(), Flower(), Something()])
    
    el.only_type(Flower).get_nearest(player)
    

    Selector 相同

    【讨论】:

      猜你喜欢
      • 2015-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多