# 111. Python's io Module

The `io` module in Python provides the tools for working with various I/O operations such as reading from and writing to files, memory buffers, and other file-like objects. Below are 10 Python code snippets that demonstrate how to use the `io` module for efficient I/O operations:

**1. Reading from a File with `io`**

You can use the `open()` function from the `io` module to read from a file.

```python
import io

# Open a file for reading
with io.open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content)
```

**Explanation**:

* `io.open()` is used to open a file in the specified mode (e.g., `'r'` for reading) and encoding (e.g., `'utf-8'`).
* It behaves similarly to the built-in `open()` function but with added flexibility for various types of I/O operations.

***

**2. Writing to a File with `io`**

To write data to a file, use the `io.open()` function with write mode.

```python
import io

# Write to a file
with io.open('output.txt', 'w', encoding='utf-8') as f:
    f.write("Hello, World!")
    print("Data written to the file.")
```

**Explanation**:

* `io.open()` is used with the `'w'` mode to open the file for writing.
* If the file doesn’t exist, it will be created.

***

**3. Buffered I/O with `io.BytesIO`**

The `io.BytesIO` class allows you to work with byte data in memory, simulating a file.

```python
import io

# Create a bytes buffer
byte_buffer = io.BytesIO()

# Write bytes to the buffer
byte_buffer.write(b"Hello, BytesIO!")

# Move the cursor to the beginning of the buffer to read
byte_buffer.seek(0)

# Read the content from the buffer
content = byte_buffer.read()
print(content.decode())  # Decoding bytes to string
```

**Explanation**:

* `io.BytesIO()` creates an in-memory buffer for bytes data, which can be treated as a file-like object.
* You can write bytes and seek to different positions to read data.

***

**4. Text I/O with `io.StringIO`**

`io.StringIO` provides a way to work with strings as file-like objects.

```python
import io

# Create a StringIO object
string_buffer = io.StringIO()

# Write to the string buffer
string_buffer.write("Hello, StringIO!")

# Move to the beginning of the string buffer to read
string_buffer.seek(0)

# Read the content
content = string_buffer.read()
print(content)
```

**Explanation**:

* `io.StringIO()` allows you to read and write strings in memory.
* This is useful when you want to treat strings like files in I/O operations.

***

**5. Reading Lines from a File**

You can read lines from a file with `io` using a buffered approach.

```python
import io

# Open a file for reading
with io.open('example.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(line.strip())  # Strip to remove the trailing newline
```

**Explanation**:

* Reading lines from a file can be done using a `for` loop, which automatically handles line breaks.

***

**6. Writing Lines to a File**

Write multiple lines to a file using `io`.

```python
import io

# Open a file for writing
with io.open('output.txt', 'w', encoding='utf-8') as f:
    lines = ["First line\n", "Second line\n", "Third line\n"]
    f.writelines(lines)
    print("Lines written to the file.")
```

**Explanation**:

* `writelines()` is used to write multiple lines to a file. Each line must include a newline character () if required.

***

**7. Memory-Mapped File I/O with `mmap`**

The `mmap` module can be used for memory-mapped file I/O, allowing you to map files directly into memory.

```python
import mmap

# Open a file for memory-mapped access
with open('example.txt', 'r+') as f:
    # Create a memory-map to the file
    mm = mmap.mmap(f.fileno(), 0)
    
    # Read data from the memory-mapped file
    print(mm[:5])  # Read first 5 bytes
    mm.close()
```

**Explanation**:

* `mmap()` maps a file into memory, enabling fast and efficient access.
* The `fileno()` function returns the file descriptor used by `mmap()`.

***

**8. Binary File Operations with `io`**

For binary file reading and writing, you can use the `rb` or `wb` modes with `io.open()`.

```python
import io

# Open a binary file for writing
with io.open('example.bin', 'wb') as f:
    f.write(b'Hello, binary file!')
    print("Binary data written to file.")
```

**Explanation**:

* `'wb'` mode is used for writing binary data to a file.
* The `b` in the `b'Hello'` indicates that the data is in bytes.

***

**9. Buffered File Reading and Writing**

You can use `io.BufferedReader` and `io.BufferedWriter` for efficient I/O operations when dealing with large files.

```python
import io

# Open a file for buffered reading
with io.open('large_file.txt', 'rb') as f:
    buffer = io.BufferedReader(f)
    data = buffer.read(1024)  # Read in chunks of 1024 bytes
    print(data)
```

**Explanation**:

* `io.BufferedReader()` provides a buffer to read large files in chunks, improving performance for large datasets.

***

**10. Creating Custom File-Like Objects**

You can create your custom file-like objects by subclassing `io.IOBase`.

```python
import io

class MyFileLike(io.IOBase):
    def __init__(self, content):
        self.content = content
        self.position = 0

    def read(self, size=-1):
        if self.position >= len(self.content):
            return ''
        result = self.content[self.position:self.position+size]
        self.position += len(result)
        return result

# Create an instance of MyFileLike
my_file = MyFileLike('Hello, custom file-like object!')

# Read from the custom object
print(my_file.read(5))  # Output: Hello
```

**Explanation**:

* By subclassing `io.IOBase`, you can create your own file-like object with custom read, write, and other file operations.

***

**Key Takeaways:**

* **`io.open()`**: Allows reading and writing files with encoding and various modes.
* **`BytesIO`**: Works with bytes data in memory.
* **`StringIO`**: Works with string data in memory.
* **`BufferedReader` / `BufferedWriter`**: Efficient buffered I/O operations.
* **`mmap`**: Provides memory-mapped file I/O for large files.
* **Binary and Text I/O**: Use `rb`, `wb`, `r`, and `w` modes for binary and text files.
* **Custom File-Like Objects**: Subclass `io.IOBase` to create custom file-like objects.

These operations are powerful tools in Python for handling I/O tasks, whether you're working with files, memory buffers, or custom data sources.
