set()的key参数有Python实现吗?

时间:2018-08-01 13:56:27

标签: python set

问题

我想知道是否有使用set()参数实现key的python函数,类似于例如key中的sorted(iterable, *, key=None, reverse=False)

我认为此功能在某些情况下很有用,尤其是在比较自定义对象而不必更改其hash()函数的情况下。

所需效果

示例1

iterable = [1.0, 1.5, 2.0, 2.5]
x = set(iterable)                 # yields {1.0, 1.5, 2.0, 2.5}
y = set(iterable, key=math.floor) # yields {1.0, 2.0}

示例2

iterable = ['abc', 'ade', 'bbc', 'abc', 'bfg']
x = set(iterable)                     # yields {'abc', 'ade', 'bbc', 'bfg'}
y = set(iterable, key=lambda x: x[0]) # yields {'abc', 'bbc'}

选择哪个

我可以想象这将不会实现,因为如果发现重复对象,很难选择选择哪个对象。但是,这可以通过某些策略来克服,例如“选择第一”,“选择随机”,“选择最后”。

答案

感谢您的所有答复,Daniel Roseman提出了针对该问题的最优雅的解决方案:

key = math.floor # Or any desired key function
y   = set({key(i): i for i in iterable}.values())

4 个答案:

答案 0 :(得分:3)

这基本上不是字典,键是将函数应用于值的结果吗?

y = {math.floor(i): i for i in iterable}

答案 1 :(得分:3)

您可以使用自定义类型来实现另一种方法:

class FlooredFloat:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(math.floor(self.value))

    def __eq__(self, other):
        return other.value == self.value


iterable = [1.0, 1.5, 2.0, 2.5]
x = set(map(FlooredFloat, iterable))

答案 2 :(得分:0)

您可以使其具有功能:

import itertools
import math


def strategy(items):
    return items[0]


def set_with_key(iterable, key):
    iterable = sorted(iterable, key=key)
    groups = [list(x[1]) for x in itertools.groupby(iterable, key)]
    return [strategy(items) for items in groups]


iterable = [1.0, 1.5, 2.0, 2.5]
print(set_with_key(iterable, math.floor))

iterable = ["abc", "ade", "bbc", "abc", "bfg"]
print(set_with_key(iterable, lambda x: x[0]))

答案 3 :(得分:0)

Daniel Roseman's observation使用字典非常出色。

如果从语法上讲,您需要看起来像键函数的东西,那么编写起来就很容易了:

def key(it, func):
    seen=set()
    for e in it:
        x=func(e)
        if x not in seen:
            seen.add(x)
            yield True
        else:
            yield False

然后,您可以对它(和zip)使用集合理解:

>>> iterable = [1.0, 1.5, 2.0, 2.5]
>>> {x for x,y in zip(iterable, key(iterable, math.floor)) if y}
{1.0, 2.0}
>>> {x for x,y in zip(iterable, key(iterable, lambda s: s[0])) if y}
{'abc', 'bbc'}

您还可以这样编写函数:

def set_with_key(it, func):
    seen=set()
    return {e for e in iterable if func(e) not in seen and not seen.add(func(e))}

>>> set_with_key([1.0, 1.5, 2.0, 2.5], math.floor)
{1.0, 2.0}
>>> set_with_key(['abc', 'ade', 'bbc', 'abc', 'bfg'], lambda s: s[0])
{'abc', 'bbc'}