2. Inspection#
Everything in Python is an object. You can inspect objects! Objects usually contain help information, and you can see what’s available, and often can even find the source all through inspection.
2.1. Inspecting#
You can inspect objects. There are lots of ways.
In a Jupyter notebook, use
object.<tab>
to bring up completions, shift tab for help.You can use
dir(object)
to see all attributes (more or less)You can use
help(object)
orobject?
(IPython only) to see helpYou can
import inspect
and use the tools thereYou can install the rich library and use
rich.inspect()
def f(x: float) -> float:
"""I am a square!"""
return x**2
The help of an object includes its signature and its docstring:
help(f)
Help on function f in module __main__:
f(x: float) -> float
I am a square!
You can see a list of methods (or use <tab>
in iPython or the Python REPL, but underscored methods often require you start by typing an underscore first):
dir(f)
['__annotations__',
'__builtins__',
'__call__',
'__class__',
'__closure__',
'__code__',
'__defaults__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__get__',
'__getattribute__',
'__globals__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__kwdefaults__',
'__le__',
'__lt__',
'__module__',
'__name__',
'__ne__',
'__new__',
'__qualname__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__']
The inspect module is a built-in module that can provide a lot of other information:
import inspect
print(inspect.getsourcefile(f))
print()
print(inspect.getsource(f))
# WARNING! THIS DOES NOT ALWAYS WORK!
/tmp/ipykernel_2705/260819024.py
def f(x: float) -> float:
"""I am a square!"""
return x**2
WARNING: You cannot always see the source of a function, so this is a user trick, not one to use in a library!
Python does a three stage procedure when interpreting. It converts source to bytecode (pyc files), then runs the bytecode in the interpreter. When loading a file that has been run before (or came from a wheel, more on that later), it only loads the bytecode if the source hasn’t changed - the source is not re-parsed. So inspect works by looking up the original file location. But you can delete the original file and run from bytecode only! Don’t do that, but you can. Also, you can run from a zip file, and the original file might not be openable. And, finally, when running live in a REPL, there may not be a source (it works in IPython for us, though).
import rich
rich.inspect(f)
╭─────────────── <function f at 0x7fa7916fd090> ───────────────╮ │ def f(x: float) -> float: │ │ │ │ I am a square! │ │ │ │ 36 attribute(s) not shown. Run inspect(inspect) for options. │ ╰──────────────────────────────────────────────────────────────╯
rich.inspect(3)
╭────── <class 'int'> ───────╮ │ int([x]) -> integer │ │ int(x, base=10) -> integer │ │ │ │ ╭────────────────────────╮ │ │ │ 3 │ │ │ ╰────────────────────────╯ │ │ │ │ denominator = 1 │ │ imag = 0 │ │ numerator = 3 │ │ real = 3 │ ╰────────────────────────────╯
Try adding different keyword arguments to
rich.inspect
. Shift-tab in IPython to see options.methods=True
on the int, for example.