Functions related to classes — Introspection documentation

# Functions related to classes

introspection.iter_subclasses(cls, include_abstract=False)

  Yields subclasses of the given class.

  New in version 1.5.

  Parameters

    - cls (`type`) – A base class

    - include_abstract (`bool`, default: `False`) – Whether abstract base classes should be included

  Return type

    `Iterator`[`type`]

  Returns

    An iterator yielding subclasses

introspection.get_subclasses(*args, **kwargs)

  Collects all subclasses of the given class.

  Parameters

    - cls – A base class

    - include_abstract – Whether abstract base classes should be included

  Return type

    `Set`[`type`]

  Returns

    A set of all subclasses

introspection.get_attributes(obj, include_weakref=False)

  Returns a dictionary of all of `obj`’s attributes. This includes attributes stored in the object’s `__dict__` as well as in `__slots__`.

  Parameters

    - obj (`Any`) – The object whose attributes will be returned

    - include_weakref (`bool`, default: `False`) – Whether the value of the `__weakref__` slot should be included in the result

  Return type

    `Dict`[`str`, `Any`]

  Returns

    A dict of `{attr_name: attr_value}`

introspection.get_abstract_method_names(cls)

  Returns a set of names of abstract methods (and other things) in the given class. See also `is_abstract()`.

  New in version 1.4.

  Parameters

    cls (`type`) – A class

  Return type

    `Set`[`str`]

  Returns

    The names of all abstract methods in that class

introspection.safe_is_subclass(subclass, superclass)

  A clone of `issubclass()` that returns `False` instead of throwing a `TypeError`.

  New in version 1.2.

  Parameters

    - subclass – The subclass

    - superclass – The superclass

  Return type

    `bool`

  Returns

    Whether `subclass` is a subclass of `superclass`

introspection.iter_slots(cls)

  Iterates over all `__slots__` of the given class, yielding `(slot_name, slot_descriptor)` tuples.

  If a slot name is used more than once, all of them will be yielded in the order they appear in the class’s MRO.

  Note that this function relies on the class-level `__slots__` attribute - deleting or altering this attribute in any way may yield incorrect results.

  Parameters

    cls (`type`) – The class whose slots to yield

  Return type

    `Iterator`[`Tuple`[`str`, `Any`]]

  Returns

    An iterator yielding `(slot_name, slot_descriptor)` tuples

introspection.get_slot_names(cls)

  Collects all of the given class’s `__slots__`, returning a set of slot names.

  Parameters

    cls (`type`) – The class whose slots to collect

  Return type

    `Set`[`str`]

  Returns

    A set containing the names of all slots

introspection.get_slot_counts(cls)

  Collects all of the given class’s `__slots__`, returning a dict of the form `{slot_name: count}`.

  Parameters

    cls (`type`) – The class whose slots to collect

  Return type

    `Dict`[`str`, `int`]

  Returns

    A `collections.Counter` counting the number of occurrences of each slot

introspection.get_slots(cls)

  Collects all of the given class’s `__slots__`, returning a dict of the form `{slot_name: slot_descriptor}`.

  If a slot name is used more than once, only the descriptor that shadows all other descriptors of the same name is returned.

  Parameters

    cls (`type`) – The class whose slots to collect

  Return type

    `Dict`[`str`, `Any`]

  Returns

    A dict mapping slot names to descriptors

introspection.get_implicit_method_type(method_name)

  Given the name of a method as input, returns what kind of method python automatically converts it to. The return value can be `staticmethod`, `classmethod`, or `None`.

  Examples:

      >>> get_implicit_method_type('frobnicate_quadrizzles')
      >>> get_implicit_method_type('__new__')
      <class 'staticmethod'>
      >>> get_implicit_method_type('__init_subclass__')
      <class 'classmethod'>

  New in version 1.3.

  Parameters

    method_name (`str`) – The name of a dundermethod

  Return type

    `Literal`[None, <class ‘classmethod’>, <class ‘staticmethod’>]

  Returns

    The type of that method

introspection.fit_to_class(thing, cls, name=None)

  Updates `thing`‘s metadata to match `cls`‘s.

  `thing` can be one of the following:

  - A function

  - A `staticmethod`

  - A `classmethod`

  - A `property`

  If `name` is not `None`, `thing` will be renamed accordingly.

  New in version 1.4.

  Parameters

    - thing – The thing whose metadata should be updated

    - cls (`type`) – The class to copy the metadata from

    - name (`Optional`[`str`], default: `None`) – The name to rename `thing` to

introspection.add_method_to_class(method, cls, name=None, method_type=auto)

  Adds `method` to `cls`‘s namespace under the given `name`.

  If `name` is `None`, it defaults to `method.__name__`.

  The method’s metadata (`__name__`, `__qualname__`, and `__module__`) will be updated to match the class.

  If a `method_type` is passed in, it should have a value of `staticmethod`, `classmethod`, or `None`. If omitted, it is automatically determined by calling `get_implicit_method_type()`. The method is then automatically wrapped with the appropriate descriptor.

  New in version 1.3.

  Parameters

    - method – The method to add to the class

    - cls (`type`) – The class to which to add the method

    - name (`Optional`[`str`], default: `None`) – The name under which the method is registered in the class namespace

    - method_type – One of `staticmethod`, `classmethod`, or `None` (or omitted)

  Return type

    `None`

introspection.wrap_method(method, cls, name=None, method_type=auto)

  Adds `method` to `cls`‘s namespace under the given `name`, wrapping the existing method (if one exists).

  The replaced method will be passed in as the first positional argument (even before the implicit `self`). If the class previously didn’t implement this method, an appropriate dummy method will be passed in instead, which merely sends the call further up the MRO.

  `method_type` has the same meaning as it does in `add_method_to_class()`.

  Example:

      class Foo:
          def __init__(self, foo):
              self.foo = foo

          def __repr__(self):
              return f'<Foo object with foo={self.foo}>'

      def custom_init(original_init, self, *args, **kwargs):
          original_init(self, *args, **kwargs)
          print('Initialized instance:', self)

      wrap_method(custom_init, Foo, '__init__')

      Foo(5)  # prints "Initialized instance: <Foo object with foo=5>"

  Note

  Adding a `__new__` method to a class can lead to unexpected problems because of the way `object.__new__` works.

  If a class doesn’t implement a `__new__` method at all, `object.__new__` silently discards any arguments it receives. But if a class does implement a custom `__new__` method, passing arguments into `object.__new__` will throw an exception:

      class ThisWorks:
          def __init__(self, some_arg):
              pass

      class ThisDoesntWork:
          def __new__(cls, *args, **kwargs):
              return super().__new__(cls, *args, **kwargs)

          def __init__(self, some_arg):
              pass

      ThisWorks(5)  # works
      ThisDoesntWork(5)  # throws TypeError: object.__new__() takes exactly one argument

  This is why, when this function is used to add a `__new__` method to a class that previously didn’t have one, it automatically generates a dummy `__new__` that attempts to figure out whether it should forward its arguments to the base class’s `__new__` method or not. This is why the following code will work just fine:

      class ThisWorks:
          def __init__(self, some_arg):
              pass

      def __new__(original_new, cls, *args, **kwargs):
          return original_new(cls, *args, **kwargs)

      wrap_method(__new__, ThisWorks)

      ThisWorks(5)  # works!

  However, it is impossible to always correctly figure out if the arguments should be passed on or not. If there is another `__new__` method that passes on its arguments, things will go wrong:

      class Parent:
          def __init__(self, some_arg):
              pass

      class Child(Parent):
          def __new__(cls, *args, **kwargs):
              return super().__new__(cls, *args, **kwargs)

      def __new__(original_new, cls, *args, **kwargs):
          return original_new(cls, *args, **kwargs)

      wrap_method(__new__, Parent)

      Parent(5)  # works!
      Child(5)  # throws TypeError

  In such a scenario, the method sees that `Child.__new__` exists, and therefore it is `Child.__new__`‘s responsibility to handle the arguments correctly. It should consume all the arguments, but doesn’t, so an exception is raised.

  As a workaround, you can mark `Child.__new__` as a method that forwards its arguments. This is done by setting its `_forwards_args` attribute to `True`:

      Child.__new__._forwards_args = True

      Child(5)  # works!

  New in version 1.3.

  Parameters

    - method – The method to add to the class

    - cls – The class to which to add the method

    - name – The name under which the method is registered in the class namespace

    - method_type – One of `staticmethod`, `classmethod`, or `None` (or omitted)