Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit inheritance from the collections.abc.Container class when implementing __contains__ method

So, I am a bit new to Python, and also a bit new to OOP paradigm, and recently I've encountered something that seems a bit counter-intuitive. For instance, look at the code below (run on Python 3.9):

from collections.abc import Container
class OddContainer:
    
    def __contains__(self, x):
        if not isinstance(x, int) or not x%2:
            return False
        return True
            
if __name__ == '__main__':
    
    x = OddContainer()
    print(isinstance(x, Container))

The print here will output True, indicating that OddContainer is a subclass of Container, but OddContainer class did not explicitly specify its superclasses. Can somebody tell me what exactly is going on here? Is it correct to say that any class that has a __contains__() method overloaded internally becomes subclass of the Container class, or is that a naive interpretation? If the former is correct, than are there any other cases where this inheritance through method overloading occurs?

Thanks in advance.

like image 886
Akhaim Avatar asked Dec 07 '25 06:12

Akhaim


2 Answers

TL;DR isinstance is not based purely on inheritance. The class being tested can define for itself what it means to be an instance of the class. (Don't confuse "x is an instance of C" with "the type of x inherits from C".)


Container defines __subclasshook__ to cause any class with a __contains__ method to be considered a subclass, whether or not Container is an actual ancestor of the class via inheritance.

Here is the full definition of collections.abc.Container](https://github.com/python/cpython/blob/3.9/Lib/_collections_abc.py#L388) as of Python 3.9:

class Container(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            return _check_methods(C, "__contains__")
        return NotImplemented

    __class_getitem__ = classmethod(GenericAlias)

__subclasshook__ takes precedence over the inheritance check.

When deciding if some class C is subclass of Container, Container.__subclasshook__(C) is called. If C.__contains__ exists, it returns True; if not, it returns False. (I'm not sure it's worth diving into how Container.__subclasshook__ could be called and cls not be Container.)

like image 153
chepner Avatar answered Dec 08 '25 20:12

chepner


From the docs of isinstance:

Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof.

Emphasis mine. When following the link of "virtual", it leads to a description of "ABC" in the glossary that points out:

ABCs introduce virtual subclasses, which are classes that don’t inherit from a class but are still recognized by isinstance() and issubclass(); see the abc module documentation.

So, OddContainer is considered to be a "virtual subclass" since it fulfills the Container ABC.

like image 45
Carcigenicate Avatar answered Dec 08 '25 21:12

Carcigenicate



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!