Python 使用条件从集合中弹出一个值

Python 使用条件从集合中弹出一个值,python,collections,Python,Collections,我有一组对象,我想使用lambda表达式或某种条件删除该组的值 我会这样做: def pop(container, cond): value = None removed = False for x in container: if cond(x): value = x removed = True container.remove(x) break

我有一组对象,我想使用lambda表达式或某种条件删除该组的值

我会这样做:

def pop(container, cond):
    value = None
    removed = False

    for x in container:
        if cond(x):
            value = x
            removed = True
            container.remove(x)
            break

     if not removed:
         raise Exception('No value to pop')

     return value
一个示例用例是:

compare_object = {"price": 100, "quantity": 1, "product_id": 2}
objs = set([....])

def comparison(obj):
    def wrap(obj2):
        return (
            obj2['price'] == obj['price'] and
            obj2['quantity'] == obj['quantity'] and
            obj2['product_id'] == obj['product_id']
        )


similar_obj = pop(objs, comparison(compare_object))
通过这种方式,我们可以从一个集合中获得一个相似的对象,因此在调用该方法后,该集合将被缩减


我想知道,是否有一种更简单的方法可以使用python中已有的实际数据结构来实现这一点,而不是在集合上循环,这在大型集合上可能会很昂贵?

而不是复杂的
for
循环,您可以根据该条件创建
筛选器
(或者
itertools.ifilter
for python 2)然后从该过滤器中删除
下一个
元素

def pop(container, cond):
    try:
        value = next(filter(cond, container))
        container.remove(value)
        return value
    except StopIteration:
        raise Exception("No Value to pop")
例如:

>>> lst = [1,2,3,4,5]
>>> cond = lambda x: x % 2 == 0
>>> pop(lst, cond)
2
>>> pop(lst, cond)
4
>>> pop(lst, cond)
Exception: No Value to pop
>>> lst
[1, 3, 5]
或者,您可以通过立即返回找到的值来简化函数:

def pop(container, cond):
    for x in container:
        if cond(x):
            container.remove(x)
            return x
    raise Exception('No value to pop')
您的
pop()。您可以将代码简化为:

compare_object = {"price": 100, "quantity": 1, "product_id": 2}
objs = set([....])

def comparison(obj):
    def wrap(obj2):
        return (
            obj2['price'] == obj['price'] and
            obj2['quantity'] == obj['quantity'] and
            obj2['product_id'] == obj['product_id']
        )

similar_obj = next(filter(comparison(compare_object), objs))
如果没有匹配项,这将引发
StopIteration


至于性能:如果您有一个集合,根据定义它是无序的,并且您的条件必须针对每个条目进行检查,那么迭代所有元素是没有办法的。但是,由于您只检索第一次出现的事件,因此实际上不需要对整个集合进行迭代。由于
filter()
惰性地评估其结果,因此它的行为方式相同,只在集合中迭代,直到找到第一个匹配项。

出于某些原因,我决定使用
defaultdict
变量。而不是使用
集合
。我只是将数据存储在带有列表的defaultdict中

设置容器:

container = defaultdict(list)
for exp_line in exp_lines:
    container[(
        exp_line.unit_amount,
        exp_line.unit_quantity,
        exp_line.product_id,
        exp_line.uom_id
    )].append(exp_line)
obj = container[(
    -(aal.amount/aal.unit_amount),
    aal.unit_amount,
    aal.product_id,
    aal.product_uom_id
)].pop()
从容器中弹出:

container = defaultdict(list)
for exp_line in exp_lines:
    container[(
        exp_line.unit_amount,
        exp_line.unit_quantity,
        exp_line.product_id,
        exp_line.uom_id
    )].append(exp_line)
obj = container[(
    -(aal.amount/aal.unit_amount),
    aal.unit_amount,
    aal.product_id,
    aal.product_uom_id
)].pop()
我看到这个方法的唯一缺点是,没有简单的方法知道容器是否为空。但是,尝试从空列表中弹出将引发一个异常,正如预期的那样,数据将实际从容器中删除

由于条件是相当“固定”的,就像我对集合使用单个条件一样,因此这种方式简单得多。但是如果我必须使用不能散列的“自定义”键。我想其他带有“下一个/过滤器”的答案将是一个更好的工具


真的没有算法。

好的。。。那么问题是什么呢?有错误吗?不,我想知道我是在重新发明轮子,还是有一个数据结构已经在这样做了。我不认为有一个像内置集合一样的对象可以做到这一点
set
确实有一个
.pop
方法,但它不能让您控制删除哪些项…我看到的唯一问题是,我在集合上循环。但是除非条件与项的哈希值相关,除非您提前知道条件并构建某种辅助索引,否则没有很好的方法将条件映射到特定项。我喜欢这样,过滤器更简单,因为它返回一个迭代器,我猜它不会扫描整个列表。最终使用defaultdict进行扫描,因为我的条件可以作为列表dict的键生成。但这不会
pop
集合中的元素,对吗?此外,OP的原始代码也会在找到第一个匹配项时立即停止,使用
break
。对,它不会停止。但是,由于从集合中删除元素是
O(1)
,可以通过调用
objs.remove(类似的\u obj)
来完成,而无需花费太多额外的成本。我的版本也会在第一次出现时停止,因为
next()
只在
filter
对象上调用一次,该对象计算lazylright,我可能读错了,或者可能是第一个版本的措辞有点偏离。我想你的意思是OP的版本不会这么做。