2

I am trying to understand how to implement a list or a hash of objects in Python. More specifically, in this question, which was the closest I could get, I saw that by doing:

class MyClass(object):

def __init__(self):
    self.numbers = [1,2,3,4,54]

def __contains__(self, key):
    return key in self.numbers

>>> m = MyClass()
>>> 54 in m
True

I could use the operator in directly on the class to check an attribute inside of it.

My question is an extension of this one. Suppose I have a list or a hash of MyClass objects, that is:

list = [m1,m2,m3]
hash = {m1,m2,m3}

I would like to use something like:

if 54 in list: 
   #do something
if 54 in hash: 
   #do something

What methods are necessary to be defined in MyClass so that the in operator works? I am unsure what in does (if is it a == that would require an __eq or something else). If you happen to know how one would make .index() work in such case I would appreciate as well.

Edit: Sorry the initial question was confusing. I hope I made it clear. I am trying to find an attribute that is part of the class, such as the original example of the other question. But now I am trying to do so inside a list or a hash. In a sense, I would like that by asking if an element is inside a list, it asks if it is inside each element of the list. I am unsure if by using the operator in on a list or a hash it does that, or it compares using ==.

Thank you.

Community
  • 1
  • 1
  • It is called a *mapping* in Python terminology. The `dict` type, or dictionary, is the default implementation. When you talk about hashes, then you are talking about the opaque values used when storing keys in a mapping. – Martijn Pieters Apr 17 '13 at 17:24
  • This question is far from clear. If you do a list of objects of your class, you just use the operator "in" in that list - you don have to provide nothing on the class. To use in Python `sets` (that you are calling "hashes") just add a "__hash__" method to your class that returns an integer. – jsbueno Apr 17 '13 at 17:24
  • Note that your `hash` name defines a *`set()`* instead, not a mapping. You only have no key-value pairs. – Martijn Pieters Apr 17 '13 at 17:25
  • Apologizes, I made a typo over the question I am correcting it now. What I want is to convention an identifier which is an attribute of the class. – Oeufcoque Penteano Apr 17 '13 at 17:27

2 Answers2

4

When in tests for containment, it'll check for equality against your custom type.

In other words, ob in some_sequence can be seen as:

any((ob is contained_ob or ob == contained_ob) for contained_ob in some_sequence)

Two custom classes ob1 and ob2 test as equal if:

  1. They are identical, ob1 is ob2 is True. They are the same object.
  2. If ob1 has an __eq__ method, then ob1.__eq__(ob2) is tested.
  3. If ob1 has no such method, but ob2.__eq__() does exist then ob2.__eq__(ob1) is tried.

You can also define the __ne__() and / or __cmp__() methods to make things a little more complicated, but you generally want to avoid toying with this too much in custom types.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • while the any()-line is not quite correct, the rest is precise. An object x where x.__eq__(x) yields false is still `in` [x]. +1 – ch3ka Apr 17 '13 at 17:33
  • Isn't it more like `any(ob is c_ob or ob == c_ob.. etc`)? I thought identity was tested first. Actually, come to think of it, I think the documentation is wrong on this point-- it says "For the list and tuple types, ``x in y`` is true if and only if there exists an index *i* such that ``x == y[i]`` is true", but a quick test suggests that even if I force `__eq__` to raise, identity is still tested before equality, and `np.nan in [np.nan]` is true. – DSM Apr 17 '13 at 17:34
  • @Martjin so if I understood your answer correct, for testing if 54 in [m1,m2,m3] where each m is an object of MyClass, what I need to define is __eq__ for MyClass, and not __contains__. Is that correct? – Oeufcoque Penteano Apr 17 '13 at 17:35
  • 1
    @OeufcoquePenteano: Correct, yes. – Martijn Pieters Apr 17 '13 at 17:38
0

As @MartijnPieters said, you should overload the __eq__() method. I'm not sure what you mean by the hash/dictionary example you gave.

class MyClass(object):

    def __init__(self):
        self.numbers = [1,2,3,4,54]

    def __contains__(self, key):
        return key in self.numbers

    def __eq__(self, other):
        if other in self.numbers:
            return True
        else:
            return False


m1 = MyClass()
m2 = MyClass()
m3 = MyClass()

test1 = 54 in [m1, m2, m3]
print test1
#True
test2 = 666 in [m1, m2, m3]
print test2
#False
reptilicus
  • 10,290
  • 6
  • 55
  • 79