Python Introspection

From NovaOrdis Knowledge Base
Revision as of 22:54, 15 May 2024 by Ovidiu (talk | contribs) (→‎hasattr())
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Internal

TODO

caller_frame = inspect.stack()[2]
code_context = caller_frame.code_context[0].strip()

 if "something.some_method" in code_context:
     return self._something_else.get_something_else(arg1=something, arg2=something, called_by="something.some_method")

Overview

Introspection is Python's equivalent for Java reflection. It is the ability to determine the type of an object at runtime and to dynamically examine Python objects.

inspect Standard Library Module

https://docs.python.org/3/library/inspect.html#module-inspect

inspect is a Standard Library module.

Introspect Members of an Object Instance

All members of an object instance can be identified using inspect.getmembers(). It its most generic form, without any argument, the function returns all members of an object instance as (name, value) tuples, sorted by name. The result will include the reference to the object instance's class, as __class__, instance attributes, methods, etc.:

import inspect

class SampleClass:
    def __init__(self):
        self.color = 'blue'

i = SampleClass()

print(f'{"name".ljust(17)} {"value".ljust(83)} value type')

for name, value in inspect.getmembers(i):
    print(f'{name.ljust(17)} {str(value).ljust(83)} {str(type(value))}')

produces:

name              value                                                                               value type
__class__         <class '__main__.SampleClass'>                                                      <class 'type'>
__delattr__       <method-wrapper '__delattr__' of SampleClass object at 0x109144190>                 <class 'method-wrapper'>
__dict__          {'color': 'blue'}                                                                   <class 'dict'>
__dir__           <built-in method __dir__ of SampleClass object at 0x109144190>                      <class 'builtin_function_or_method'>
__doc__           None                                                                                <class 'NoneType'>
__eq__            <method-wrapper '__eq__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__format__        <built-in method __format__ of SampleClass object at 0x109144190>                   <class 'builtin_function_or_method'>
__ge__            <method-wrapper '__ge__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__getattribute__  <method-wrapper '__getattribute__' of SampleClass object at 0x109144190>            <class 'method-wrapper'>
__gt__            <method-wrapper '__gt__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__hash__          <method-wrapper '__hash__' of SampleClass object at 0x109144190>                    <class 'method-wrapper'>
__init__          <bound method SampleClass.__init__ of <__main__.SampleClass object at 0x109144190>> <class 'method'>
__init_subclass__ <built-in method __init_subclass__ of type object at 0x7faabab12f60>                <class 'builtin_function_or_method'>
__le__            <method-wrapper '__le__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__lt__            <method-wrapper '__lt__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__module__        __main__                                                                            <class 'str'>
__ne__            <method-wrapper '__ne__' of SampleClass object at 0x109144190>                      <class 'method-wrapper'>
__new__           <built-in method __new__ of type object at 0x10984c950>                             <class 'builtin_function_or_method'>
__reduce__        <built-in method __reduce__ of SampleClass object at 0x109144190>                   <class 'builtin_function_or_method'>
__reduce_ex__     <built-in method __reduce_ex__ of SampleClass object at 0x109144190>                <class 'builtin_function_or_method'>
__repr__          <method-wrapper '__repr__' of SampleClass object at 0x109144190>                    <class 'method-wrapper'>
__setattr__       <method-wrapper '__setattr__' of SampleClass object at 0x109144190>                 <class 'method-wrapper'>
__sizeof__        <built-in method __sizeof__ of SampleClass object at 0x109144190>                   <class 'builtin_function_or_method'>
__str__           <method-wrapper '__str__' of SampleClass object at 0x109144190>                     <class 'method-wrapper'>
__subclasshook__  <built-in method __subclasshook__ of type object at 0x7faabab12f60>                 <class 'builtin_function_or_method'>
__weakref__       None                                                                                <class 'NoneType'>
color             blue                                                                                <class 'str'>

Introspect Members of an Object Instance Filtering with Predicates

inspect.getmembers() accepts a predicate as the second argument. The predicate is a function applied to the value that must return true or false. The inspect package comes with several predefined predicates:

Predicate Description
inspect.isclass Return true if the value of the member is a class
inspect.ismethod Return true if the value of the member is an instance method
inspect.ismodule Return true if the value of the member is a module
import inspect

class SampleClass:
    def __init__(self):
        self.color = 'blue'

    def some_method(self):
        print('some_method')

i = SampleClass()

for (name, value) in inspect.getmembers(i, inspect.ismethod):
    print(f' method name: {name}')

will display:

method name: __init__
method name: some_method

Introspecting the Calls Stack

inspect.stack()

returns a list of records for the stack above the caller's frame. The caller's frame is in the position 0 in the result, and more distant frames have increasingly bigger indices.

Builtin Introspection Functions

getattr()

getattr() is a built-in function that returns the value of the specified attribute from the specified object. If an attribute with such name does not exist, an AttributeError exception is thrown.

Class Introspection with getattr()

A specific static method can be identified by querying the class attributes with inspect.getmembers() and selecting the matching method, or by name with getattr() builtin.

cls = ...
method_name = 'some_static_method'
try:
  method = getattr(cls, method_name)
  print(method)
except AttributeError:
  print(f"{method_name} method not found in class {cls}")

hasattr()

hasattr() is a built-in function that checks whether the specified attribute exists.

hasattr(module_or_package, '__path__')

setattr()

Invoke Functions and Methods Dynamically

Dynamic Invocation

Specialized Introspection

Module Internal Representation and Introspection

Module Internal Representation and Introspection