转换为自定义集合集 class returns 空集 Python

Conversion to set of custom collections class returns empty set in Python

我最近编写了自己的 OrderedSet 实现,因为我在使用公开可用的 ordered/sorted 集实现时遇到了问题。 class 在后台使用字典代理对象,主要是转发操作。我实施了所有(在我看来)相关的方法,包括。 __iter__,并且像 list(myset) 这样的调用按预期工作。

但是调用set(myset)总是returns空集

这是 OrderedSet 的完整代码:

from typing import TypeVar, Generic, Optional, Iterable, Set, AbstractSet, Union, Iterator, Any, Dict

T = TypeVar("T")
S = TypeVar("S")


class OrderedSet(Generic[T], Set[T]):
    def __init__(self, base: Optional[Union[Dict[T, None], Iterable[T]]] = None):
        super().__init__()

        self.the_dict: Dict[T, None]
        if not base:
            self.the_dict = {}
        elif isinstance(base, dict):
            self.the_dict = base
        else:
            self.the_dict = dict.fromkeys(base)

    def __eq__(self, o: object) -> bool:
        return isinstance(o, OrderedSet) and list(self.the_dict) == list(o.the_dict)

    def __ne__(self, o: object) -> bool:
        return not self.__eq__(o)

    def __str__(self) -> str:
        return "{" + ", ".join(list(map(str, self.the_dict))) + "}"

    def __repr__(self) -> str:
        return f"OrderedSet({repr(self.the_dict)})"

    def add(self, element: T) -> None:
        self.the_dict = {**self.the_dict, **{element: None}}

    def clear(self) -> None:
        self.the_dict.clear()

    def copy(self) -> 'OrderedSet[T]':
        return OrderedSet(self.the_dict.copy())

    def difference(self, s: Iterable[Any]) -> 'OrderedSet[T]':
        return OrderedSet({e: None for e in self.the_dict if e not in s})

    def difference_update(self, s: Iterable[Any]) -> None:
        self.the_dict = {e: None for e in self.the_dict if e not in s}

    def discard(self, element: T) -> None:
        del self.the_dict[element]

    def intersection(self, s: Iterable[Any]) -> 'OrderedSet[T]':
        return OrderedSet({e: None for e in self.the_dict if e in s})

    def intersection_update(self, s: Iterable[Any]) -> None:
        self.the_dict = {e: None for e in self.the_dict if e in s}

    def isdisjoint(self, s: Iterable[Any]) -> bool:
        return self.the_dict.keys().isdisjoint(s)

    def issubset(self, s: Iterable[Any]) -> bool:
        return set(iter(self)).issubset(iter(s))

    def issuperset(self, s: Iterable[Any]) -> bool:
        return set(iter(self)).issuperset(iter(s))

    def pop(self) -> T:
        items = list(self.the_dict)
        result = items.pop()
        self.the_dict = dict.fromkeys(items)
        return result

    def remove(self, element: T) -> None:
        del self.the_dict[element]

    def symmetric_difference(self, s: Iterable[T]) -> 'OrderedSet[T]':
        return OrderedSet(
            dict.fromkeys([e for e in self.the_dict if e not in s] +
                          [e for e in s if e not in self.the_dict]))

    def symmetric_difference_update(self, s: Iterable[T]) -> None:
        self.the_dict = self.symmetric_difference(s).the_dict

    def union(self, s: Iterable[T]) -> 'OrderedSet[T]':
        return OrderedSet({**self.the_dict, **dict.fromkeys(s)})

    def update(self, s: Iterable[T]) -> None:
        self.the_dict = self.union(s).the_dict

    def __len__(self) -> int:
        return len(self.the_dict)

    def __contains__(self, o: object) -> bool:
        return o in self.the_dict

    def __iter__(self) -> Iterator[T]:
        return iter(self.the_dict)

    def __and__(self, s: AbstractSet[object]) -> 'OrderedSet[T]':
        return self.intersection(s)

    def __iand__(self, s: AbstractSet[object]) -> 'OrderedSet[T]':
        result = self.intersection(s)
        self.the_dict = result.the_dict
        return result

    def __or__(self, s: AbstractSet[S]) -> 'OrderedSet[Union[T, S]]':
        return self.union(s)

    def __ior__(self, s: AbstractSet[S]) -> 'OrderedSet[Union[T, S]]':
        result = self.union(s)
        self.the_dict = result.the_dict
        return result

    def __sub__(self, s: AbstractSet[Optional[T]]) -> 'OrderedSet[T]':
        return self.difference(s)

    def __isub__(self, s: AbstractSet[Optional[T]]) -> 'OrderedSet[T]':
        result = self.difference(s)
        self.the_dict = result.the_dict
        return result

    def __xor__(self, s: AbstractSet[S]) -> 'OrderedSet[Union[T, S]]':
        return self.symmetric_difference(s)

    def __ixor__(self, s: AbstractSet[S]) -> 'OrderedSet[Union[T, S]]':
        result = self.symmetric_difference(s)
        self.the_dict = result.the_dict
        return result

    def __le__(self, s: AbstractSet[object]) -> bool:
        return self.issubset(s)

    def __lt__(self, s: AbstractSet[object]) -> bool:
        return self.issubset(s) and len(self) < len(s)

    def __ge__(self, s: AbstractSet[object]) -> bool:
        return set(iter(self)) >= set(iter(s))

    def __gt__(self, s: AbstractSet[object]) -> bool:
        return set(iter(self)) > set(iter(s))

这里有一些示例调用:

>>> from orderedset import OrderedSet
>>> s = OrderedSet([3, 1, 2])
>>> s
OrderedSet({3: None, 1: None, 2: None})
>>> list(s)
[3, 1, 2]
>>> set(s)
set()

有人知道这里发生了什么吗? set() 函数似乎是用冗长的 C 代码实现的,我对它的理解还不够深入,无法推断出任何东西。不过,似乎 __iter__ 被要求转换为 list,而不是 set...

有什么想法吗?

非常感谢!

Python 3.9 或以上

您的 class 应该继承自 collections.abc.Set instead of typing.Set(无论如何已被弃用)

from collections.abc import Set
...
class OrderedSet(Generic[T], Set[T]):
    ...

obj = OrderedSet()
obj.add(1)
obj.add(2)
obj.add(3)
print(set(obj)) # {1,2,3}

Python 3.8 或更低

在 3.9 之前,无法将泛型与 collections.abc.Set 一起使用。从 AbstractSet 继承解决了问题。

from typing import Generic, AbstractSet
...
class OrderedSet(Generic[T], AbstractSet[T]):
    ...

您需要初始化super 否则它将是一个空集

    def __init__(self, base: Optional[Union[Dict[T, None], Iterable[T]]] = None):
        self.the_dict: Dict[T, None]
        if not base:
            self.the_dict = {}
        elif isinstance(base, dict):
            self.the_dict = base
        else:
            self.the_dict = dict.fromkeys(base)
        super().__init__(self.the_dict.keys())