# 216. Method Chaining with Self-Returning Methods

#### **🔹 1. Basic Method Chaining**

```python
class User:
    def __init__(self, name):
        self.name = name
        self.email = None

    def set_email(self, email):
        self.email = email
        return self  # Returning self enables chaining

    def display(self):
        print(f"User: {self.name}, Email: {self.email}")
        return self  # Returning self allows further chaining

user = User("Alice").set_email("alice@example.com").display()
```

🔍 **Key Takeaway:**

* Each method returns `self`, allowing multiple calls in **one line**.

***

#### **🔹 2. Chaining Methods for a Fluent API**

```python
class FluentMath:
    def __init__(self, value=0):
        self.value = value

    def add(self, x):
        self.value += x
        return self

    def subtract(self, x):
        self.value -= x
        return self

    def multiply(self, x):
        self.value *= x
        return self

    def result(self):
        print(f"Result: {self.value}")
        return self

math = FluentMath().add(10).subtract(3).multiply(2).result()
```

🔍 **Key Takeaway:**

* This pattern is common in **mathematical operations** or **data transformations**.

***

#### **🔹 3. Using Method Chaining in a Configuration Builder**

```python
class Config:
    def __init__(self):
        self.settings = {}

    def set(self, key, value):
        self.settings[key] = value
        return self

    def show(self):
        print(self.settings)
        return self

config = Config().set("debug", True).set("theme", "dark").show()
```

🔍 **Key Takeaway:**

* Used for **builder patterns**, where configuration is set dynamically.

***

#### **🔹 4. Method Chaining for String Processing**

```python
class StringProcessor:
    def __init__(self, text):
        self.text = text

    def to_upper(self):
        self.text = self.text.upper()
        return self

    def replace(self, old, new):
        self.text = self.text.replace(old, new)
        return self

    def show(self):
        print(self.text)
        return self

processor = StringProcessor("hello world").to_upper().replace("WORLD", "Python").show()
```

🔍 **Key Takeaway:**

* Useful in **text processing**, making transformations **clearer**.

***

#### **🔹 5. Chaining for Logging System**

```python
class Logger:
    def __init__(self):
        self.logs = []

    def log(self, message):
        self.logs.append(message)
        return self

    def show(self):
        print("\n".join(self.logs))
        return self

Logger().log("Starting program").log("Processing data").log("Finished execution").show()
```

🔍 **Key Takeaway:**

* **Logging chains** make debugging **simpler and cleaner**.

***

#### **🔹 6. Method Chaining for HTTP Requests (Mock Example)**

```python
class Request:
    def __init__(self):
        self.url = None
        self.method = "GET"
        self.headers = {}

    def set_url(self, url):
        self.url = url
        return self

    def set_method(self, method):
        self.method = method
        return self

    def add_header(self, key, value):
        self.headers[key] = value
        return self

    def send(self):
        print(f"Sending {self.method} request to {self.url} with headers {self.headers}")
        return self

Request().set_url("https://api.example.com").set_method("POST").add_header("Auth", "Bearer token").send()
```

🔍 **Key Takeaway:**

* Mimics real-world APIs like **Requests**, improving readability.

***

#### **🔹 7. Chaining in File Operations**

```python
class FileHandler:
    def __init__(self, filename):
        self.filename = filename
        self.content = ""

    def write(self, text):
        self.content += text + "\n"
        return self

    def save(self):
        with open(self.filename, "w") as f:
            f.write(self.content)
        return self

FileHandler("sample.txt").write("Hello").write("World!").save()
```

🔍 **Key Takeaway:**

* **Fluent API for file handling** can simplify file writing.

***

#### **🔹 8. Using Method Chaining in a Fluent Query Builder**

```python
class QueryBuilder:
    def __init__(self):
        self.query = "SELECT * FROM table"

    def where(self, condition):
        self.query += f" WHERE {condition}"
        return self

    def order_by(self, column):
        self.query += f" ORDER BY {column}"
        return self

    def execute(self):
        print(f"Executing: {self.query}")
        return self

query = QueryBuilder().where("age > 18").order_by("name").execute()
```

🔍 **Key Takeaway:**

* **Chained SQL queries** improve readability.

***

#### **🔹 9. Chaining in Data Pipelines**

```python
class DataPipeline:
    def __init__(self, data):
        self.data = data

    def filter(self, condition):
        self.data = [x for x in self.data if condition(x)]
        return self

    def sort(self):
        self.data.sort()
        return self

    def display(self):
        print(self.data)
        return self

DataPipeline([5, 3, 8, 1, 2]).filter(lambda x: x > 2).sort().display()
```

🔍 **Key Takeaway:**

* Useful for **data transformations**.

***

#### **🔹 10. Chaining with Decorators**

```python
class Profile:
    def __init__(self, name):
        self.name = name
        self.bio = ""

    def set_bio(self, bio):
        self.bio = bio
        return self

    def show(self):
        print(f"Profile: {self.name}, Bio: {self.bio}")
        return self

@staticmethod
def create(name):
    return Profile(name)

Profile.create("John").set_bio("Software Engineer").show()
```

🔍 **Key Takeaway:**

* **Combining method chaining with decorators** for user-friendly creation.

***

#### **🚀 Summary: When to Use Method Chaining?**

| **Use Case**                  | **Example**                                                       |
| ----------------------------- | ----------------------------------------------------------------- |
| **Configuration Builders**    | `Config().set("key", "value").set("debug", True).show()`          |
| **Data Processing Pipelines** | `DataPipeline(data).filter(condition).sort().display()`           |
| **String Transformations**    | `StringProcessor("text").to_upper().replace("old", "new").show()` |
| **Logging Systems**           | `Logger().log("Step 1").log("Step 2").show()`                     |
| **SQL Query Builders**        | `QueryBuilder().where("id=1").order_by("name").execute()`         |
| **HTTP Requests**             | `Request().set_url("api.com").set_method("POST").send()`          |
