Python Introspection: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(42 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Internal=
=Internal=
* [[Python_Language_OOP#Class_Introspection|Python Object-Oriented Programming]]
* [[Python_Language_OOP#Class_Introspection|Python Object-Oriented Programming]]
=TODO=
* https://www.geeksforgeeks.org/code-introspection-in-python/
* The 'inspect' package and:
<syntaxhighlight lang='py'>
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")
</syntaxhighlight>


=Overview=
=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.
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.
=Introspect the Members of an Object Instance=
=<tt>inspect</tt> Standard Library Module=
All members of an object instance can be identified using <code>inspect.getmembers()</code>.
{{External|https://docs.python.org/3/library/inspect.html#module-inspect}}
<code>[[Python_Language#inspect|inspect]]</code> is a Standard Library module.
==Introspect Members of an Object Instance==
All members of an object instance can be identified using <code>inspect.getmembers()</code>. 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 <code>__class__</code>, [[Python_Language_OOP_Attributes_and_Properties#Instance_Attributes|instance attributes]], [[Python_Language_OOP#Methods|methods]], etc.:
<syntaxhighlight lang='py'>
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))}')
</syntaxhighlight>
produces:
<font size=-2>
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'>
</font>
===Introspect Members of an Object Instance Filtering with Predicates===
<code>inspect.getmembers()</code> accepts a predicate as the second argument. The predicate is a function applied to the value that must return <code>true</code> or <code>false</code>. The <code>inspect</code> package comes with several predefined predicates:
 
{| class="wikitable" style="text-align: left;"
!Predicate
!Description
|-
|<code>inspect.isclass</code> || Return true if the value of the member is a class
|-
|<code>inspect.ismethod</code> || Return true if the value of the member is an instance method
|-
|<code>inspect.ismodule</code>  || Return true if the value of the member is a module
|}


=<tt>getattr()</tt>=
<syntaxhighlight lang='py'>
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}')
</syntaxhighlight>
will display:
<font size=-2>
method name: __init__
method name: some_method
</font>
 
==Introspecting the Calls Stack==
 
<syntaxhighlight lang='py'>
inspect.stack()
</syntaxhighlight>
 
returns a list of records for the [[Python_Language_Functions#Call_Stack|stack]] above the caller's [[Python_Language_Functions#Frame|frame]]. The caller's frame is in the position 0 in the result, and more distant frames have increasingly bigger indices.
 
=Builtin Introspection Functions=
 
==<tt>getattr()</tt>==
<code>[[Python Language Functions#getattr|getattr()]]</code> 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 <code>[[Python Language Exceptions#AttributeError |AttributeError]]</code> exception is thrown.
<code>[[Python Language Functions#getattr|getattr()]]</code> 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 <code>[[Python Language Exceptions#AttributeError |AttributeError]]</code> exception is thrown.
 
===Class Introspection with <tt>getattr()</tt>===
==Class Introspection with <tt>getattr()</tt>==
A specific static method can be identified by querying the class attributes with <code>[[#getmembers.28.29|inspect.getmembers()]]</code> and selecting the matching method, or by name with <code>[[#getattr.28.29|getattr()]]</code> builtin.
A specific static method can be identified by querying the class attributes with <code>[[#getmembers.28.29|inspect.getmembers()]]</code> and selecting the matching method, or by name with <code>[[#getattr.28.29|getattr()]]</code> builtin.
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
Line 21: Line 123:
   print(f"{method_name} method not found in class {cls}")
   print(f"{method_name} method not found in class {cls}")
</syntaxhighlight>
</syntaxhighlight>
==<tt>hasattr()</tt>==
<code>[[Python Language Functions#hasattr|hasattr()]]</code> is a built-in function that checks whether the specified attribute exists.


=The <tt>inspect</tt> Standard Library Module=
{{External|https://docs.python.org/3/library/inspect.html#module-inspect}}
<code>[[Python_Language#inspect|inspect]]</code> is a Standard Library module.
==<tt>getmembers()</tt>==
<code>getmembers()</code> returns all members of an object as (name, value) pairs sorted by name. The object can be a module, class, etc. A predicate can be provided to the function, and the function returns only return members that satisfy a given predicate.
===Module Introspection with <tt>getmembers()</tt>===
Also see: {{Internal|Python_Language_Modularization#Module_Introspection|Python Language Modularization}}
Find classes in a module:
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
import inspect
hasattr(module_or_package, '__path__')
 
module = sys.modules['some_package.some_subpackage.SomeClass']
class_tuples = inspect.getmembers(module, inspect.isclass)
for ct in class_tuples:
  print('class name: ', ct[0])
  print('class: ', ct[1])
</syntaxhighlight>
</syntaxhighlight>
==<tt>setattr()</tt>==


=Invoking Functions and Methods Dynamically=
=<span id='Invoking_Functions_and_Methods_Dynamically'></span>Invoke Functions and Methods Dynamically=
{{Internal|Python_Language_Functions#Dynamic_Invocation|Dynamic Invocation}}
{{Internal|Python_Language_Functions#Dynamic_Invocation|Dynamic Invocation}}
=TODO=
=Specialized Introspection=
https://www.geeksforgeeks.org/code-introspection-in-python/
==Module Internal Representation and Introspection==
==Module Internal Representation and Introspection==
{{Internal|Python Module Internal Representation and Introspection#Overview|Module Internal Representation and Introspection}}
{{Internal|Python Module Internal Representation and Introspection#Overview|Module Internal Representation and Introspection}}

Latest revision as of 22:54, 15 May 2024

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