Reputation: 5160
https://gist.github.com/rjurney/1e8454af8e44312d02d7
class FrozenSortedTuple:
"""A frozenset that cares about order of tuples. And is not actually frozen."""
def __init__(self, vals):
if type(vals) not in [list, set, tuple]:
raise Exception('Value not a list or set')
self.vals = list(vals)
def __eq__(self, other):
if len(self.vals) != len(other):
return False
if type(self.vals) != type(other):
return False
if type(other) not in [list, set, tuple]:
return False
other_list = list(other)
for i,item in enumerate(self.vals):
if item != other_list[i]:
return False
return True
Called in iPython:
In [2]: a = ['a','b']
In [3]: b = ['b','a']
In [4]: c = ['a','b']
In [5]: a == b
Out[5]: False
In [6]: FrozenSortedTuple(a)
Out[6]: <__main__.FrozenSortedTuple instance at 0x103c56200>
In [7]: fa = FrozenSortedTuple(a)
In [8]: fb = FrozenSortedTuple(b)
In [9]: fa == fb
Error:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-9-317181571e4d> in <module>()
----> 1 fa == fb
<ipython-input-1-ef99f0af5061> in __eq__(self, other)
15
16 def __eq__(self, other):
---> 17 if len(self.vals) != len(other):
18 return False
19 if type(self.vals) != type(other):
AttributeError: FrozenSortedTuple instance has no attribute '__len__'
I'm confused.
Upvotes: 2
Views: 834
Reputation: 5160
Thanks, based on the other answers, what i wanted is this:
class FrozenSortedTuple:
"""A frozenset that cares about order of tuples. And is not actually frozen."""
def __init__(self, vals):
if type(vals) not in [list, set, tuple]:
raise Exception('Value not a list or set')
self.vals = vals
def __len__(self):
return len(self.vals)
def __iter__(self):
return iter(self.vals)
def __getitem__(self, key):
return list(self.vals)[key]
def __eq__(self, other):
if len(self) != len(other):
print "len(self)"
return False
for i,item in enumerate(self.vals):
if item != other[i]:
return False
return True
def __str__(self):
str_val = str()
for val in self:
str_val += str(val)
return str_val
def __hash__(self):
return hash(str(self))
Tests:
# FrozenSortedTuple Tests
a = ['a','b']
b = ['b','a']
c = ['a','b']
fa = FrozenSortedTuple(a)
fb = FrozenSortedTuple(b)
fc = FrozenSortedTuple(c)
fa == fb
fa == fc
fa == ['a','b']
fa == ['b','a']
fa == ('a','b')
fa == ('b','a')
a = set([fa, fb, fc])
b = set([fa, fb, fc])
c = set([fa, fc, fb])
a == b
b == c
fa in a
fb in b
d = set([fb])
fa in d
Upvotes: 1
Reputation: 789
The way you define __eq__(self, other)
, equality can only be achieved when other is an instance of the type you are wrapping, i.e. list, set, or tuple. You trigger the error by comparing two instances of FrozenSortedTuple
. The error message tells you that len() cannot be invoked on such an instance, and that is because you have not defined the method __len__(self)
in your class.
If you define __len__()
for your class, it will work. See the Python Documentation (this links to the 2.7 docs, but it should work the same in Python 3.x)
Alternatively, you could compare len(self.vals) == len(other.vals)
to compare FrozenSortedTuple
instances.
Upvotes: 2
Reputation: 76244
If you're trying to directly compare the structure and contents of the values of two FrozenSortedTuples, change all your instances of other
to other.vals
.
def __eq__(self, other):
if len(self.vals) != len(other.vals):
return False
if type(self.vals) != type(other.vals):
return False
if type(other.vals) not in [list, set, tuple]:
return False
other_list = list(other.vals)
for i,item in enumerate(self.vals):
if item != other_list[i]:
return False
return True
Of course, this will fail to work if other
isn't a FrozenSortedTuple. For instance, fa == 23
won't work because the number 23 doesn't have a "vals" attribute.
Upvotes: 3