- 5.1 Function Definitions
- 5.2 Default Arguments
- 5.3 Variadic Arguments
- 5.4 Keyword Arguments
- 5.5 Variadic Keyword Arguments
- 5.6 Functions Accepting All Inputs
- 5.7 Positional-Only Arguments
- 5.8 Names, Documentation Strings, and Type Hints
- 5.9 Function Application and Parameter Passing
- 5.10 Return Values
- 5.11 Error Handling
- 5.12 Scoping Rules
- 5.13 Recursion
- 5.14 The lambda Expression
- 5.15 Higher-Order Functions
- 5.16 Argument Passing in Callback Functions
- 5.17 Returning Results from Callbacks
- 5.18 Decorators
- 5.19 Map, Filter, and Reduce
- 5.20 Function Introspection, Attributes, and Signatures
- 5.21 Environment Inspection
- 5.22 Dynamic Code Execution and Creation
- 5.23 Asynchronous Functions and await
- 5.24 Final Words: Thoughts on Functions and Composition
5.21 Environment Inspection
Functions can inspect their execution environment using the built-in functions globals() and locals(). globals() returns the dictionary that’s serving as the global namespace. This is the same as the func.__globals__ attribute. This is usually the same dictionary that’s holding the contents of the enclosing module. locals() returns a dictionary containing the values of all local and closure variables. This dictionary is not the actual data structure used to hold these variables. Local variables can come from outer functions (via a closure) or be defined internally. locals() collects all of these variables and puts them into a dictionary for you. Changing an item in the locals() dictionary has no effect on the underlying variable. For example:
def func(): y = 20 locs = locals() locs['y'] = 30 # Try to change y print(locs['y']) # Prints 30 print(y) # Prints 20
If you wanted a change to take effect, you’d have to copy it back into the local variable using normal assignment.
def func(): y = 20 locs = locals() locs['y'] = 30 y = locs['y']
A function can obtain its own stack frame using inspect.currentframe(). A function can obtain the stack frame of its caller by following the stack trace through f.f_back attributes on the frame. Here is an example:
import inspect def spam(x, y): z = x + y grok(z) def grok(a): b = a * 10 # outputs: {'a':5, 'b':50 } print(inspect.currentframe().f_locals) # outputs: {'x':2, 'y':3, 'z':5 } print(inspect.currentframe().f_back.f_locals) spam(2, 3)
Sometimes you will see stack frames obtained using the sys._getframe() function instead. For example:
import sys def grok(a): b = a * 10 print(sys._getframe(0).f_locals) # myself print(sys._getframe(1).f_locals) # my caller
The attributes in Table 5.2 can be useful for inspecting frames.
Table 5.2 Frame Attributes
Attribute |
Description |
---|---|
f.f_back |
Previous stack frame (toward the caller) |
f.f_code |
Code object being executed |
f.f_locals |
Dictionary of local variables (locals()) |
f.f_globals |
Dictionary used for global variables (globals()) |
f.f_builtins |
Dictionary used for built-in names |
f.f_lineno |
Line number |
f.f_lasti |
Current instruction. This is an index into the bytecode string of f_code. |
f.f_trace |
Function called at start of each source code line |
Looking at stack frames is useful for debugging and code inspection. For example, here’s an interesting debug function that lets you view the values of the selected variables of the caller:
import inspect from collections import ChainMap def debug(*varnames): f = inspect.currentframe().f_back vars = ChainMap(f.f_locals, f.f_globals) print(f'{f.f_code.co_filename}:{f.f_lineno}') for name in varnames: print(f' {name} = {vars[name]!r}') # Example use def func(x, y): z = x + y debug('x','y') # Shows x and y along with file/line return z