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)