2016-06-27 2 views
3

У меня есть некоторый код, в котором ребро представлены в виде кортежаВозможно ли переопределить равный оператор для кортежей?

(vertex_1, vertex_2) 

и у меня есть списки ребер, которые представляют собой плоские встроенные лицо, как и для примера ниже.

мне нужно искать, если ребро присутствует в списке, но мне нужно вернуться верно как если использовать (v1, v2) и (v2, v1):

f1 = [(6, 1), (1, 2), (2, 7), (7, 6)] 
(6,1) in f1 
(1,6) in f1 

True 
False 
+6

Вы можете использовать 'set' или' frozenset' (hashable). –

+0

Я reeeally новичок в python! –

+0

Да: подкласс или сделать свой собственный простой класс. Нет: функция по умолчанию '__eq__' и' __contains__' не может быть изменена для существующих объектов tuple, это встроенная функция, привязанная к объекту класса –

ответ

3

Вы не можете переопределить метод равенства для существующих типов, поэтому вам придется создать свой собственный тип, который затем потребует замены всех существующих кортежей на ваш собственный тип.

Если ваша главная проблема заключается только (6,1) in f1 случай использования, то, возможно, вы просто должны рассмотреть вопрос о создании способа, который вместо того, чтобы:

def contains(t, lst): 
    return (t[0], t[1]) in lst or (t[1], t[0]) in lst 

И тогда вы можете просто использовать его как это:

>>> f1 = [(6, 1), (1, 2), (2, 7), (7, 6)] 
>>> contains((6, 1), f1) 
True 
>>> contains((1, 6), f1) 
True 

Это по существу имеет то преимущество, что вместо этого вам не нужно заменять ваши кортежи другим типом. Таким образом, вы можете работать со всеми своими источниками данных так, как они есть.

1

Вы должны сделать кортеж подкласс и изменить его метод равенства (__eq__):

class UnorderedTuple(tuple): 
    def __eq__(self, other): 
     return len(self) == len(other) and set(self) == set(other) 

будет работать для вашего случая с (кортежами длиной == 2, если кортеж элементы являются hashable - то есть неизменен и имеет четко определенные сравнения)

Чтобы ваш список кортежей преобразован в список Unordered кортежей сделать:

f1 = [UnorderedTuple(f_) for f_ in f1] 

Чтобы иметь надлежащее удержание запроса (in оператора) над списком может быть медленным, - так что лучше иметь набор чем список:

set_f1 = { UnorderedTuple(f_) for f_ in f1 } 
(6,1) in set_f1 
(1,6) in set_f1 

Эта реализация не будет очень производительной, так как он создает новый set для каждого сравнения. Так что, если ваши кортежи всегда будет F два элемента, более производительным, чтобы иметь метод __eq__ unroled нравится:

def __eq__(self, other): 
     return super(UnordoredTuple, self).__eq__(other) or (self[0] == other[1] and self[1] == other[0]) 
+1

Неправильно наследовать от кортежа здесь. Следует использовать совершенно отдельный тип, поэтому этот новый тип не случайно используется для элементов, не содержащих двух элементов. – poke

+0

Это зависит от потребностей O.P. - если это большая система, эти типы будут использоваться снова и снова да. Если это небольшой скрипт фильтрации, который будет работать на всегда одни и те же наборы данных, нет проблем с этим более простым решением. – jsbueno

+0

«... хотя практичность превосходит чистоту». - обратите внимание, что даже такие типы последовательности встроенных Python подчиняются этому - оставляя специализированные реализации контейнеров тем, кто их заботится, например, btress, matrixes и т. д. Вы реализуете то, что вам нужно. – jsbueno

1

«Можно ли переопределить равный оператор для кортежей»

Вроде. Вы не можете сделать это на основной tuple типа, но вы можете его подкласса:

class MyTuple(tuple): 
    def __eq__(self, other): 
     orig_eq = super(MyTuple, self).__eq__(other) 
     if orig_eq and orig_eq is not NotImplemented: 
      return True 
     else: 
      return super(MyTuple, self).__eq__(other[::-1]) 

Вообще, это, вероятно, не самый лучший подход. В зависимости от ограничений задачи, вы можете попробовать набор frozenset:

f1_set = {frozenset(tup) for tup in f1} 
frozenset((1, 6)) in f1_set 

Преимущество здесь в том, что если вы делаете несколько тестов членства в одних и тех же данных, вы, вероятно, получить лучшее время выполнения (каждый членский тест в списке O(N), и вам нужно сделать до двух для каждого элемента, который вы хотите проверить, тогда как у вас есть только один шаг O(N) для сборки f1_set, а затем каждый тест на членство - O(1)).

0

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

class new_tuple: 
    ... 

чем вы должны использовать :

tuple = (1,6) 
tuple = new_tuple(tuple) 

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

def check(tuple_, list_): 
    v1, v2 = tuple_ 
    if (v1, v2) in list_ or (v2, v1) in list_: 
     return True 
    return False 

f1 = [(6, 1), (1, 2), (2, 7), (7, 6)] 
print(check((6, 1), f1)) # this prints True 
print(check((1, 6), f1)) # this prints True 
0

Общее решение этой проблемы заключается в использовании multisets, которые представляют собой множества, где элемент может появляться более одного раза. Модуль collections определяет класс Counter, который является подклассом dict, который реализует мультимножество. Клавиши dict являются элементами мультимножества, а значения - это количество раз, когда клавиши появляются.

Это позволяет избежать ограничений на количество элементов в мультимножестве и уже доступно. Главный недостаток заключается в том, что нет «замороженной», хешируемой версии, о которой я знаю.

Примеры:

>>> from collections import Counter 

>>> Counter((3, 6, 2, 4, 2, 8)) == Counter((8, 4, 3, 6, 2, 2)) 
True 

>>> Counter((3, 6, 2, 4, 2, 8)) == Counter((8, 4, 3, 6, 4, 2)) 
False 

Вы можете использовать Counter класс непосредственно, который, вероятно, самый простой, но если вы хотите сохранить лежащую tuple представление, вы можете использовать Counter класс для реализации более общий вариант tuple подкласс предложили другие:

class MultisetTuple(tuple): 
    def __eq__(self, other): 
     return Counter(self) == Counter(other) 

Примеры:

>>> MultisetTuple((3, 6, 2, 4, 2, 8)) == MultisetTuple((8, 4, 3, 6, 2, 2)) 
True 

>>> MultisetTuple((3, 6, 2, 4, 2, 8)) == MultisetTuple((8, 4, 3, 6, 4, 2)) 
False 
Смежные вопросы