# 103. Python's inspect Module

**1. Getting Information About Functions Using `getmembers`**

```python
import inspect

def sample_function(x, y=10):
    """This is a sample function."""
    return x + y

# Get all the members of the function, including its parameters and docstring
members = inspect.getmembers(sample_function)
print(members)
```

**Explanation**: `inspect.getmembers` returns a list of all the members of the `sample_function`, including its parameters, docstring, and other information.

***

**2. Retrieving Function Source Code Using `getsource`**

```python
import inspect

def sample_function(x, y=10):
    """This is a sample function."""
    return x + y

# Get the source code of the function
source_code = inspect.getsource(sample_function)
print(source_code)
```

**Explanation**: `inspect.getsource` retrieves the source code of the `sample_function` as a string, which can be useful for debugging or introspection.

***

**3. Inspecting the Function's Signature Using `signature`**

```python
import inspect

def sample_function(x, y=10):
    """This is a sample function."""
    return x + y

# Get the signature of the function
signature = inspect.signature(sample_function)
print(f"Signature of function: {signature}")
```

**Explanation**: `inspect.signature` retrieves the function signature, including the parameters and default values. It's useful for checking how a function should be called.

***

**4. Inspecting the Parameters of a Function Using `parameters`**

```python
import inspect

def sample_function(x, y=10, z=5):
    """This is a sample function."""
    return x + y + z

# Get the function's signature
sig = inspect.signature(sample_function)

# Get the parameters from the signature
params = sig.parameters
print(f"Parameters: {params}")
```

**Explanation**: The `parameters` attribute of a signature object returns an ordered mapping of parameter names to their details, including default values.

***

**5. Checking if a Function is a Generator Using `isgeneratorfunction`**

```python
import inspect

def my_generator():
    yield 1
    yield 2

def regular_function():
    return 42

# Check if the functions are generator functions
print(inspect.isgeneratorfunction(my_generator))  # Output: True
print(inspect.isgeneratorfunction(regular_function))  # Output: False
```

**Explanation**: `inspect.isgeneratorfunction` checks if a function is a generator function (i.e., a function that uses `yield`).

***

**6. Checking if an Object is a Built-in Function or Method Using `isbuiltin`**

```python
import inspect

# Check if `print` is a built-in function
print(inspect.isbuiltin(print))  # Output: True

# Check if `sum` is a built-in function
print(inspect.isbuiltin(sum))  # Output: True
```

**Explanation**: `inspect.isbuiltin` checks if the object is a built-in function or method in Python.

***

**7. Getting the Class of a Function or Method Using `getclass`**

```python
import inspect

class MyClass:
    def my_method(self):
        pass

# Get the class of a method
method_class = inspect.getclass(MyClass.my_method)
print(method_class)  # Output: <class 'type'>
```

**Explanation**: `inspect.getclass` retrieves the class to which a function or method belongs.

***

**8. Checking if an Object is Callable Using `callable`**

```python
import inspect

def my_function():
    pass

class MyClass:
    def __call__(self):
        print("Called!")

# Checking if the objects are callable
print(callable(my_function))  # Output: True
print(callable(MyClass))  # Output: True
print(callable(MyClass()))  # Output: True
```

**Explanation**: `callable` checks if an object is callable (i.e., it can be invoked as a function). A class instance may also be callable if the `__call__` method is defined.

***

**9. Getting Information About the Stack Trace Using `stack`**

```python
import inspect

def my_function():
    # Printing the current stack trace
    stack = inspect.stack()
    for frame in stack:
        print(f"Frame info: {frame}")

my_function()
```

**Explanation**: `inspect.stack()` returns a list of `FrameInfo` objects that contain the call stack, which is helpful for debugging and tracing code execution.

***

**10. Getting Information About the Current Line of Code Using `currentframe`**

```python
import inspect

def my_function():
    current_frame = inspect.currentframe()
    print(f"Current line of code: {current_frame.f_lineno}")

my_function()
```

**Explanation**: `inspect.currentframe()` gets the current stack frame, and `f_lineno` gives the line number where the code is currently executing.

***

**Summary:**

* **`inspect.getmembers`**: Retrieve all members of an object (e.g., functions, methods, or classes).
* **`inspect.getsource`**: Get the source code of a function or method.
* **`inspect.signature`**: Retrieve the signature of a callable object (e.g., function or method).
* **`inspect.isgeneratorfunction`**: Check if a function is a generator function.
* **`inspect.isbuiltin`**: Check if an object is a built-in function.
* **`inspect.getclass`**: Retrieve the class of a function or method.
* **`callable`**: Check if an object is callable.
* **`inspect.stack`**: Retrieve the call stack information.
* **`inspect.currentframe`**: Retrieve the current stack frame.

These tools from the `inspect` module are useful for debugging, introspecting, and analyzing live objects and functions in Python.
