Phrixus
Phrixus

Reputation: 1219

Is Python Object Instance a unique object in the language?

I am wondering about how Python handles the user-defined objects. So here is the scenario:

I have created my own class called MyClass:

class MyClass():
  def __init__(self):
    # some code here

class MyClassContainer():
  def __init__(self):
    self.container = [] # will store MyClass object instances.
  def Add(self, object):
    self.container.append(object)
  def Remove(self, object):
    self.container.remove(object)

example = MyClassContainer()

myclass1 = MyClass()
example.Add(myclass1)

myclass2 = MyClass()
example.Add(myclass2)

example.Remove(myclass1)

So the question, is python's remove function able to distinguish different object instances of the same class? Is there any corner case that will not uniquely identify the object instance I wanted to delete?

A possible scenario is this one:

myclass1 = MyClass(5)
example.Add(myclass1)

myclass1 = MyClass(3)
example.Add(myclass1)

example.Remove(myclass1)

Which object instance will be removed? I guess the one with 3 passed, but is there a rule that says, python uniquely identify object instances of the same class?

Upvotes: 1

Views: 110

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123400

list.remove() removes the first object that tests equal. From the documentation:

s.remove(x)
remove the first item from s where s[i] == x

Instances of a custom class by default only test equal if they are the exact same object; if s[i] is x returns true then s[i] == x also returns true.

It is not the variable name that defines the instance; in your second example, it'll be the MyClass(3) instance that is removed from the list, because it is a unique object, distinct from the MyClass(5) instance you created and added before. You can check this with the id() function, which on CPython basically returns the current memory address:

>>> myclass1 = MyClass(5)
>>> id(myclass1)
4349144816
>>> example.Add(myclass1)
>>> myclass1 = MyClass(3)
>>> id(myclass1)
4349145040
>>> example.Add(myclass1)
>>> example.container
[<__main__.MyClass object at 0x1033aaef0>, <__main__.MyClass object at 0x1033aafd0>]
>>> hex(id(myclass1))
'0x1033aafd0'
>>> myclass1 is example.container[1]
True
>>> example.Remove(myclass1)
>>> example.container
[<__main__.MyClass object at 0x1033aaef0>]

Note that the default representation of your custom classes include the id() value in hexadecimal!

You can alter this behaviour by overriding the object.__eq__() method; have it return True or False based on your own criteria, or return the NotImplemented singleton if the other object is not a type your class supports being compared with (so Python can delegate to the other object).

For example, if your instances should be considered equal when their number attribute is equal, you'd implement it like this:

class MyClass():
    def __init__(self, number):
        self.number = number

    def __eq__(self, other):
        if not isinstance(other, MyClass):
            return NotImplemented
        return self.number == other.number

With that change, you can do this:

example.Add(MyClass(42))
example.Add(MyClass(81))
example.Remove(MyClass(42))

and that would remove the instance with number set to 42.

Upvotes: 2

Related Questions