Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are classobjects singletons?

If we have x = type(a) and x == y, does it necessarily imply that x is y?

Here is a counter-example, but it's a cheat:

>>> class BrokenEq(type):
...     def __eq__(cls, other):
...         return True
...     
>>> class A(metaclass=BrokenEq):
...     pass
... 
>>> a = A()
>>> x = type(a)
>>> x == A, x is A
(True, True)
>>> x == BrokenEq, x is BrokenEq
(True, False)

And I could not create a counterexample like this:

>>> A1 = type('A', (), {})
>>> A2 = type('A', (), {})
>>> a = A1()
>>> x = type(a)
>>> x == A1, x is A1
(True, True)
>>> x == A2, x is A2
(False, False)

To clarify my question - without overriding equality operators to do something insane, is it possible for a class to exist at two different memory locations or does the import system somehow prevent this?

If so, how can we demonstrate this behavior - for example, doing weird things with reload or __import__?

If not, is that guaranteed by the language or documented anywhere?


Epilogue:

# thing.py
class A:
    pass

Finally, this is what clarified the real behaviour for me (and it's supporting the claims in Blckknght answer)

>>> import sys
>>> from thing import A
>>> a = A()
>>> isinstance(a, A), type(a) == A, type(a) is A
(True, True, True)
>>> del sys.modules['thing']
>>> from thing import A
>>> isinstance(a, A), type(a) == A, type(a) is A
(False, False, False)

So, although code that uses importlib.reload could break type checking by class identity, it will also break isinstance anyway.

like image 433
wim Avatar asked Nov 25 '15 19:11

wim


1 Answers

No, there's no way to create two class objects that compare equal without being identical, except by messing around with metaclass __eq__ methods.

This behavior though is not something unique to classes. It's the default behavior for any object without an __eq__ method defined in its class. The behavior is inherited from object, which is the base class for all other (new-style) classes. It's only overridden for builtin types that have some other semantic for equality (e.g. container types which compare their contents) and for custom classes that define an __eq__ operator of their own.

As for getting two different refernces to the same class at different memory locations, that's not really possible due to Python's object semantics. The memory location of the object is its identity (in cpython at least). Another class with identical contents can exist somewhere else, but like in your A1 and A2 example, it's going to be seen as a different object by all Python logic.

like image 111
Blckknght Avatar answered Sep 21 '22 12:09

Blckknght