Python множество пересечений, любой способ вернуть элементы из большего набора?

Когда Python пересекает два набора, он всегда возвращает элементы из меньшего, что разумно почти во всех случаях, но я пытаюсь сделать наоборот.

В приведенном ниже фрагменте кода обратите внимание, что пересечение дает целое число, а не число с плавающей точкой.

[in]  >>> x = {1.0,2.0,3.0}
[in]  >>> y = {1}
[in]  >>> x.intersection(y)
[out] >>> {1}
[in]  >>> y.intersection(x)
[out] >>> {1}

Если я хочу вернуть поплавок, я должен использовать тяжелое копирование.

[in]  >>> x - y
[out] >>> {2.0,3.0}
[in]  >>> x - (x - y)
[out] >>> {1.0}

Я имею дело с гораздо большими наборами, чем в примере выше. Мой вопрос заключается в том, можно ли каким-то образом обмануть метод Python set.intersection, возвращая элементы из большего набора, или есть другой метод, который может возвратить число с плавающей запятой 1.0, помимо того, что я здесь сделал.

Причина, по которой я делаю это в первую очередь, заключается в том, что я пытаюсь реализовать замороженный словарь на чистом python, подклассифицируя frozenset. Я храню пары ключ-значение, используя подкласс кортежа, который я называю "Item", где хэш возвращает только хэш ключа. Используя приведенный ниже код, я могу создать набор с одной парой ключ-значение внутри. Затем я извлекаю атрибут "значение" и возвращаю его.

def __getitem__(self, key):
    wrapped = Item((key,),flag=False)
    if not frozenset.__contains__(self, wrapped):
        raise KeyError(key)
    matches = self - (self - {wrapped})
    for pair in frozenset.__iter__(matches):
        return pair.value

Я знаю, что копирование является причиной медлительности, потому что, когда я пытаюсь вернуть элемент, ключ которого отсутствует в словаре, я немедленно получаю KeyError, даже для наборов с 10 миллионами элементов.

1 ответ

С риском ответить на что-то отличное от того, что вы на самом деле просили (но, возможно, помочь с конечной целью)... фактически замороженный диктат действительно легко реализовать в python:

from collections import Mapping


class FrozenDict(Mapping):

    def __init__(self, *args, **kwargs):
        self._hash = None  # defer calculating hash until needed.
        self._data = dict(*args, **kwargs)

    def __getitem__(self, item):
        return self._data[item]

    def __len__(self):
        return len(self._data)

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

    def __repr__(self):
        return '{}({!r})'.format(type(self), self._data)

    def __hash__(self):
        if self._hash is not None:
            return self._hash
        # Only hashible if the items are hashible.              
        self._hash = hash(tuple(self.items()))


x = FrozenDict({'a': 'b'})
print x
x['c'] = 'Bad Bad Bad'

Конечно, это не совсем заморожено (в том же смысле, что и замороженный). Пользователь может получить доступ и изменить данные на frozendict - но тогда они заслуживают любых ошибок кода, которые они вызывают.


Чтобы ответить на ваш актуальный вопрос, единственная альтернатива, о которой я могу подумать, это определить вашу собственную функцию пересечения:

>>> s1 = set([1])
>>> s2 = set([1., 2.])
>>> def intersection(s1, s2):
...     return set(x for x in s1 if x in s2)
... 
>>> intersection(s1, s2)
set([1])
>>> intersection(s2, s1)
set([1.0])

Этот всегда возвращает наборы, но вы можете легко изменить, чтобы он возвращал frozenset или тип ввода, если вы предполагаете, что тип первого ввода имеет конструктор, который принимает только итерацию:

def intersection(s1, s2):
    output_type = type(s1)
    return output_type(x for x in s1 if x in s2)
Другие вопросы по тегам