# 97. Function Annotations for Type Hints

**1. Basic Type Hints for Arguments and Return Types**

```python
def add_numbers(a: int, b: int) -> int:
    return a + b

result = add_numbers(5, 10)
print(result)  # Output: 15
```

***

**2. Type Hinting with Optional Arguments**

```python
from typing import Optional

def greet(name: Optional[str] = None) -> str:
    return f"Hello, {name if name else 'Guest'}!"

print(greet())           # Output: Hello, Guest!
print(greet("Alice"))    # Output: Hello, Alice!
```

***

**3. Type Hints for Lists**

```python
from typing import List

def calculate_average(numbers: List[float]) -> float:
    return sum(numbers) / len(numbers)

print(calculate_average([1.5, 2.5, 3.0]))  # Output: 2.3333333333333335
```

***

**4. Type Hints for Dictionaries**

```python
from typing import Dict

def get_price(product: str, price_list: Dict[str, float]) -> float:
    return price_list.get(product, 0.0)

prices = {"apple": 1.5, "banana": 0.5}
print(get_price("apple", prices))  # Output: 1.5
print(get_price("orange", prices)) # Output: 0.0
```

***

**5. Type Hints for Tuples**

```python
from typing import Tuple

def get_coordinates() -> Tuple[float, float]:
    return (37.7749, -122.4194)

print(get_coordinates())  # Output: (37.7749, -122.4194)
```

***

**6. Type Hints for Callable (Functions as Arguments)**

```python
from typing import Callable

def execute(func: Callable[[int, int], int], x: int, y: int) -> int:
    return func(x, y)

result = execute(lambda a, b: a * b, 4, 5)
print(result)  # Output: 20
```

***

**7. Type Hints for Iterables**

```python
from typing import Iterable

def total_length(strings: Iterable[str]) -> int:
    return sum(len(s) for s in strings)

print(total_length(["hello", "world"]))  # Output: 10
```

***

**8. Custom Types**

```python
from typing import List, TypeAlias

Vector: TypeAlias = List[float]

def scale_vector(v: Vector, scalar: float) -> Vector:
    return [x * scalar for x in v]

print(scale_vector([1.0, 2.0, 3.0], 2.0))  # Output: [2.0, 4.0, 6.0]
```

***

**9. Union for Multiple Accepted Types**

```python
from typing import Union

def parse_input(value: Union[str, int]) -> int:
    if isinstance(value, str):
        return int(value)
    return value

print(parse_input("42"))  # Output: 42
print(parse_input(42))    # Output: 42
```

***

**10. Type Hints for Generators**

```python
from typing import Generator

def countdown(start: int) -> Generator[int, None, None]:
    while start > 0:
        yield start
        start -= 1

for num in countdown(5):
    print(num)  # Output: 5, 4, 3, 2, 1
```

***

These examples illustrate how type hints improve code readability and provide valuable documentation, helping developers understand the expected types of arguments and return values. Although type hints don’t enforce types at runtime, they enhance static analysis and integrate seamlessly with tools like **mypy** for type checking.
