1

I have set up a simple class called Cell

class Cell:
    def __init__(self, letter):
        self.letter = letter
        if letter == 'A' or letter == 'E' or letter == 'I' or letter == 'O' or letter == 'U' or letter == 'L' or letter == 'N' or letter == 'S' or  letter == 'T' or letter == 'R':
            self.points = 1
        elif letter == 'D' or letter == 'G':
            self.points = 2
        elif letter == 'B' or letter == 'C' or letter == 'M' or letter == 'P':
            self.points = 3
        elif letter == 'F' or letter == 'H' or letter == 'V' or letter == 'W' or letter == 'Y':
            self.points = 4
        elif letter == 'K':
            self.points = 5
        elif letter == 'J' or letter == 'X':
            self.points = 8
        elif letter == 'Q' or letter == 'Z':
            self.points = 10
        else:
            self.points = 0

And now I am trying to write a __contains__ method for the class so if I have an array of Cells I can check if a Cell of some letter is contained in the array

Tiles = []
Tiles.append(Cell("A")
Tiles.append(Cell("B")
Tiles.append(Cell("C")
Tiles.append(Cell("D")

Cell("A") in Tiles should return True

However I am confused on how to go about doing this and have found little help online or in the python documentation

James Notaro
  • 137
  • 1
  • 3
  • 10

2 Answers2

0

I am trying to write a __contains__ method for the class

No you're not. That's the question to ask when you're implementing a container, not the thing being contained.

You're not confused about how to implement __contains__ - you only implement that function on the container, which in this case is the list. You're interested on how contains works.

You need to implement __eq__ (and __hash__ and __ne__, for good measure)

I think this is expository:

class Cell:
    def __init__(self, thing):
       self.thing = thing
    def __eq__(self, other):
       return other.thing == self.thing

a = [Cell(1)]
print Cell(1) in a

There are more complete examples here: Elegant ways to support equivalence ("equality") in Python classes

I don't see a reference for how list contains works exactly, but a reference implementation is on line 437 here; basically it loops through the list and checks if the item provided is equal to any of the items in the list.

That's why we overload the '==' equality operator by overloading __eq__.

Naturally, this process is O(n) (takes time proportional to the number of items in the list - we have to check each one in the worst case). A dictionary or set can do this in O(1) "constant" time, but that requires hashing the input as a key. For this reason, it's typically customary to overload the hash function whenever you overload equality so that you don't get unexpected behaviour down the line. The __ne__ is python's "not equals" operator. Overload this too

Community
  • 1
  • 1
en_Knight
  • 5,301
  • 2
  • 26
  • 46
  • Don't forget about `__ne__`, too. – user2357112 May 13 '16 at 19:24
  • A set is also a dictionary without the values and can look-up hashable values in constant time. – Alyssa Haroldsen May 13 '16 at 19:31
  • Thanks this is a very well laid out answer. My only question is why do I need to implement a `__hash__` function if I'm implementing `eq` and `ne` – James Notaro May 13 '16 at 20:38
  • @JamesNotaro for lists, you don't, but for any data structure that hashes its contents (like a dictionary or set), "Cell(1) in container" will be hash Cell(1), and check if that hash value is in the container. In that case, even if Cell(1) is *equal* to something in the structure, because its hash value doesn't match, you get that it isn't contained – en_Knight May 13 '16 at 21:08
0

If s is a sequence, then m in s returns a bool. Now, s can also be sequence-like when the class type(s) implements the __contains__ method.

Hence obj in Tiles has nothing to do with membership in obj but in list Tiles.

Secondly, why Cell('A') in Tiles returned False after appending one before? Because they are different objects. You could introduce new semantics stating they being same when such and such satisfies, by implementing the __eq__ method.

x = Cell('A')
y = Cell('A')
x is y # False, same as id(x) == id(y)
x == y # don't really know what that means
# now enriching semantics by __eq__
x == y # True/ False

If you want to use those objects as keys, the __hash__ function should be implemented. But there are caveats. Check the docs.

C Panda
  • 3,297
  • 2
  • 11
  • 11