# Chapter 7: Modules and Packages

In Python, modules and packages help organize code, promote reusability, and manage complexity in large projects. A module is a file containing Python code, and a package is a collection of modules organized in directories.

#### Modules

A module is simply a Python file with a `.py` extension. Modules can contain functions, classes, and variables that can be reused in other scripts.

**Creating and Importing a Module**

1. **Create a Module**: Save the following as `mymodule.py`:

   ```python
   def greet(name):
       return f"Hello, {name}!"

   pi = 3.14159
   ```
2. **Import the Module**: Use the `import` keyword to use the module in another script.

   ```python
   import mymodule

   print(mymodule.greet("Alice"))  # Output: Hello, Alice!
   print(mymodule.pi)  # Output: 3.14159
   ```
3. **Import Specific Items**: Use the `from` keyword to import specific elements.

   ```python
   from mymodule import greet

   print(greet("Bob"))  # Output: Hello, Bob!
   ```
4. **Alias Modules**: Use the `as` keyword to give a module an alias.

   ```python
   import mymodule as mm

   print(mm.pi)  # Output: 3.14159
   ```

#### Built-in Modules

Python comes with a rich standard library of built-in modules. Some commonly used modules include:

| **Module** | **Description**                        |
| ---------- | -------------------------------------- |
| `math`     | Mathematical functions and constants.  |
| `os`       | Interacting with the operating system. |
| `sys`      | Accessing system-specific parameters.  |
| `random`   | Generating random numbers.             |
| `datetime` | Working with dates and times.          |

**Example:**

```python
import math

print(math.sqrt(16))  # Output: 4.0
print(math.pi)  # Output: 3.141592653589793
```

#### Packages

A package is a collection of related modules grouped into a directory. It contains an `__init__.py` file, which can be empty or include package initialization code.

**Creating a Package**

1. Create a directory named `mypackage`.
2. Add an empty `__init__.py` file to the directory.
3. Add modules to the package:
   * `module1.py`:

     ```python
     def add(a, b):
         return a + b
     ```
   * `module2.py`:

     ```python
     def subtract(a, b):
         return a - b
     ```
4. Import and use the package:

   ```python
   from mypackage import module1, module2

   print(module1.add(5, 3))  # Output: 8
   print(module2.subtract(5, 3))  # Output: 2
   ```

**Nested Packages**

Packages can contain sub-packages for better organization.

```plaintext
mypackage/
    __init__.py
    subpackage/
        __init__.py
        module.py
```

#### The `__name__` Variable

Every Python module has a special variable `__name__`. When a module is run directly, `__name__` is set to `"__main__"`. This is useful for writing code that behaves differently when a module is imported versus executed directly.

**Example:**

```python
# mymodule.py
if __name__ == "__main__":
    print("This script is being run directly.")
else:
    print("This script is being imported.")
```

#### Exercises

**Exercise 1:**

Create a module `calculator.py` with functions `add`, `subtract`, `multiply`, and `divide`. Use it in another script.

**Solution:**

```python
# calculator.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    return a * b

def divide(a, b):
    if b != 0:
        return a / b
    return "Cannot divide by zero"

# main.py
import calculator

print(calculator.add(10, 5))  # Output: 15
print(calculator.divide(10, 0))  # Output: Cannot divide by zero
```

**Exercise 2:**

Create a package `shapes` with modules `circle` and `rectangle`. Implement functions to calculate the area of each shape.

**Solution:**

```python
# shapes/circle.py
def area(radius):
    return 3.14 * radius ** 2

# shapes/rectangle.py
def area(length, width):
    return length * width

# main.py
from shapes import circle, rectangle

print(circle.area(5))  # Output: 78.5
print(rectangle.area(4, 6))  # Output: 24
```

#### Best Practices

1. Use meaningful names for modules and packages.
2. Avoid circular imports (modules importing each other).
3. Keep modules small and focused on a specific task.
4. Use packages to organize related functionality.

In the next chapter, we will explore file handling, including reading, writing, and managing files in Python.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://py.d19.in/chapter-7-modules-and-packages.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
