如何使用等效对象访问集合的元素?

发布于 2024-12-23 00:57:42 字数 697 浏览 0 评论 0原文

如果我有一个对象与 Python 集合中的一个元素比较,但不是同一个对象,是否有合理的方法来获取对集合中对象的引用?用例将使用该集来识别和共享重复数据。

示例 (Python 2.7):

>>> a = "This is a string"
>>> b = "This is a string"
>>> a is b
False
>>> a == b
True
>>> s = set((a,))
>>> b in s
True

如何使用 bs 获取对 a 的引用?我可以想到一种方法,但我不确定您得到的是 a 还是 b 是否不依赖于实现。 编辑:当 s 有多个元素时,这不起作用;交集很自然地实现了类似 [x for x insmaller_set if x inlarger_set]

>>> for x in set((b,)).intersection(s): c = x
...
>>> c is a
True

也许一个好的解决方法是使用将每个键映射到其自身的字典,而不是集合。

If I have an object that compares equal to an element of a Python set, but is not the same object, is there a reasonable way to get a reference to the object in the set? The use case would be using the set to identify and share duplicated data.

Example (Python 2.7):

>>> a = "This is a string"
>>> b = "This is a string"
>>> a is b
False
>>> a == b
True
>>> s = set((a,))
>>> b in s
True

How to get a reference to a using b and s? I can think of one way, but I'm not sure if it is not implementation-dependent whether you get a or b. EDIT: This does not work when s has more than one element; intersection is quite naturally implemented something like [x for x in smaller_set if x in larger_set]

>>> for x in set((b,)).intersection(s): c = x
...
>>> c is a
True

Perhaps a good workaround would be to use a dict that maps each key to itself, instead of the set.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

眼藏柔 2024-12-30 00:57:42

我在 python-list 上发现了类似的问题: 从设置。参考get_equivalent(container, item) (Python Recipe)有一个聪明的答案。

技巧是为“key”对象构造一个包装器对象,并使用 in 运算符检查包装器是否在集合中。如果包装器哈希值等于键,则其 __eq__ 方法可以获得对集合中对象的访问权限,并保存对其的引用。讨论中的一个要点是,对于无法识别的类型,集合元素的 __eq__ 方法必须返回 NotImplemented,否则包装器的 __eq__ 可能无法获取叫。

I found a similar question on python-list: Get item from set. There is a clever answer with reference to get_equivalent(container, item) (Python recipe).

The trick is to construct a wrapper object for the 'key' object, and check if wrapper is in the set using the in operator. If the wrapper hashes equal to the key, its __eq__ method can gain access to the object in the set, and save a reference to it. An important point from the discussion is that the __eq__ method of the set elements must return NotImplemented for unrecognized types, otherwise the wrapper's __eq__ may not get called.

最丧也最甜 2024-12-30 00:57:42

您的用例听起来像是字典的用例。使用与“外部”对象比较的对象的属性作为键,并使用所需对象本身作为值。

如果这是一个简单的用例,并且您可以进行线性搜索,但是,您可以做显而易见的事情 - 这不会太糟糕:

def get_equal(in_set, in_element):
   for element in in_set:
       if element == in_element:
           return element
   return None 

如果您需要您所要求的东西(我可以想知道一些用例) - 方法是创建一个自定义字典类,该类将集合作为其成员之一,对成员集合实现代理方法,并在字典和集合方法中保持字典和集合内容的同步。正确实施会非常耗时,但相对简单,并且时间复杂度为 O(1)。

如果必须复制对周围所有数据的引用不是问题(这是线性的,但可能比上面的直接搜索更糟糕),您可以使用

(data - (data - {key})).pop()

如下表达式:

In [40]: class A:
    ...:     def __init__(self, id, extra):
    ...:         self.id = id
    ...:         self.extra = extra
    ...:     def __eq__(self, other):
    ...:         return self.id == other.id
    ...:     def __hash__(self):
    ...:         return hash(self.id)
    ...:     def __repr__(self):
    ...:         return f"({self.id}, {self.extra})"
    ...: 
    ...: 

In [41]: data = set(A(i, "initial") for i in range(10))

In [42]: (data - (data - {A(5, None)})).pop()
Out[42]: (5, initial)

Your use case sounds like it is a use case for dictionaries. Use, as keys, the attribute of the object that compares equal to the "foreign" object, and as values the desired objects themselves.

If it is a simple use case, and you can have a linear search, however, you could do the obvious - it would not be bad:

def get_equal(in_set, in_element):
   for element in in_set:
       if element == in_element:
           return element
   return None 

If you need what exactly what you ar asking for (I can wonder some use cases for that) - the way to go is to create a custom dictionary class that has a set as one of its members, do implement proxy methods to the member set, and in both dictionary and set methods, keeps sync of both the dictionary and set contents. This would be time consuming to implement right, but relatively straightforward, and have a O(1) time.

If having to copy references to all data around is not an issue (this is linear, but probably worst than the straightforward search above), you can use the expression

(data - (data - {key})).pop()

as in:

In [40]: class A:
    ...:     def __init__(self, id, extra):
    ...:         self.id = id
    ...:         self.extra = extra
    ...:     def __eq__(self, other):
    ...:         return self.id == other.id
    ...:     def __hash__(self):
    ...:         return hash(self.id)
    ...:     def __repr__(self):
    ...:         return f"({self.id}, {self.extra})"
    ...: 
    ...: 

In [41]: data = set(A(i, "initial") for i in range(10))

In [42]: (data - (data - {A(5, None)})).pop()
Out[42]: (5, initial)
冷默言语 2024-12-30 00:57:42

这是我通过利用“eq”和“contains”方法的行为制作的快速解决方案。代码注释(希望)是自我记录的,但它在其他方面非常简单。

正如示例所示,它适用于 set、dict 和 list,理论上也适用于任何实现“contains”的对象。

import typing as _ts
from typing import Any


class Getter:
    __slots__ = "key", "value"

    def __init__(self, key, value=None):
        self.key = key
        self.value = value

    def __repr__(self):
        return "{}({}, {})".format(
            type(self).__name__,
            repr(self.key), repr(self.value),
        )

    def __hash__(self):
        return hash(self.key)

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


RAISES = object()


def getkey(keyed: _ts.Container, key: Any, default: Any = RAISES):
    getter = Getter(key)
    if getter in keyed:
        # providing '__contains__' is implemented to call
        #  the '__eq__' method (which in any sane case it
        #  should be), this results in our special
        #  'Getter.__eq__' method being called with the
        #  element we're trying to get as the 'other' argument
        return getter.value
    if default is RAISES:
        raise KeyError(key)
    return default


if __name__ == '__main__':
    # testing

    class T(int):
        def __repr__(self):
            return "T({})".format(int.__repr__(self))

    def main():
        # works for both builtin set and dict
        hm1 = {T(1), T(2), T(3)}
        hm2 = {T(1): 1, T(2): 2, T(3): 3}
        print(getkey(hm1, 2))
        print(getkey(hm2, 2))
        # should print "T(2)"

        # even works for list
        lst = [T(1), T(2), T(3)]
        print(getkey(lst, 3))
        # should print "T(3)"

        # in theory could work for any type that
        #  implements '__contains__' by calling '__eq__'

    main()

Here's a quick solution I made by exploiting the behavior of the 'eq' and 'contains' methods. Code comments are (hopefully) self documenting, but it's otherwise pretty simple.

As the example shows, it works for set, dict and list, and in theory could work for any object that implements 'contains'.

import typing as _ts
from typing import Any


class Getter:
    __slots__ = "key", "value"

    def __init__(self, key, value=None):
        self.key = key
        self.value = value

    def __repr__(self):
        return "{}({}, {})".format(
            type(self).__name__,
            repr(self.key), repr(self.value),
        )

    def __hash__(self):
        return hash(self.key)

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


RAISES = object()


def getkey(keyed: _ts.Container, key: Any, default: Any = RAISES):
    getter = Getter(key)
    if getter in keyed:
        # providing '__contains__' is implemented to call
        #  the '__eq__' method (which in any sane case it
        #  should be), this results in our special
        #  'Getter.__eq__' method being called with the
        #  element we're trying to get as the 'other' argument
        return getter.value
    if default is RAISES:
        raise KeyError(key)
    return default


if __name__ == '__main__':
    # testing

    class T(int):
        def __repr__(self):
            return "T({})".format(int.__repr__(self))

    def main():
        # works for both builtin set and dict
        hm1 = {T(1), T(2), T(3)}
        hm2 = {T(1): 1, T(2): 2, T(3): 3}
        print(getkey(hm1, 2))
        print(getkey(hm2, 2))
        # should print "T(2)"

        # even works for list
        lst = [T(1), T(2), T(3)]
        print(getkey(lst, 3))
        # should print "T(3)"

        # in theory could work for any type that
        #  implements '__contains__' by calling '__eq__'

    main()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文