# Python 函数基础

## 学习目标
- 理解函数的定义与调用
- 掌握各种参数类型：位置参数、关键字参数、默认参数、可变参数
- 理解返回值与作用域基础
- 能够编写和调用自定义函数

---

## 一、什么是函数

函数是一段可重复使用的代码块，用于完成特定任务。使用函数可以避免重复代码，提高程序的可读性和可维护性。

```python
# 定义函数
def greet():
    print("Hello, Python!")

# 调用函数
greet()   # Hello, Python!
greet()   # 可以多次调用
```

---

## 二、函数的定义与调用

### 基本语法

```python
def 函数名(参数1, 参数2, ...):
    """文档字符串（可选，用于说明函数功能）"""
    # 函数体
    return 返回值   # 可选
```

```python
def add(a, b):
    """返回两个数的和"""
    result = a + b
    return result

# 调用
sum_val = add(3, 5)
print(sum_val)   # 8

# 没有 return 的函数返回 None
def say_hello():
    print("Hello!")

result = say_hello()   # Hello!
print(result)          # None
```

---

## 三、参数类型

### 3.1 位置参数

```python
def greet(name, greeting):
    print(f"{greeting}, {name}!")

greet("Alice", "Good morning")   # Good morning, Alice!
# greet("Alice")                 # TypeError: 缺少参数
```

### 3.2 关键字参数

```python
# 调用时指定参数名，顺序可以打乱
greet(greeting="Hi", name="Bob")   # Hi, Bob!
greet(name="Charlie", greeting="Hello")   # Hello, Charlie!

# 混合使用：位置参数在前，关键字参数在后
greet("David", greeting="Hey")   # Hey, David!
```

### 3.3 默认参数

```python
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice")                  # Hello, Alice!（使用默认值）
greet("Bob", "Good morning")    # Good morning, Bob!（覆盖默认值）
```

**注意**：默认参数应该在非默认参数之后。

```python
# 错误示范
# def wrong(a=1, b):   # SyntaxError
#     pass

# 正确示范
def correct(a, b=1, c=2):
    print(a, b, c)

correct(10)          # 10 1 2
correct(10, 20)      # 10 20 2
correct(10, 20, 30)  # 10 20 30
```

**默认参数的陷阱**：

```python
# 默认参数在函数定义时求值，如果默认参数是可变对象，会有意外行为
def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item(1))   # [1]
print(add_item(2))   # [1, 2]   # 意外！保留了上一次的结果
print(add_item(3))   # [1, 2, 3]

# 正确做法：使用 None 作为默认值
def add_item_safe(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item_safe(1))   # [1]
print(add_item_safe(2))   # [2]
```

### 3.4 可变参数 *args

```python
# *args 接收任意数量的位置参数，以元组形式传入
def sum_all(*numbers):
    total = 0
    for n in numbers:
        total += n
    return total

print(sum_all())           # 0
print(sum_all(1, 2))       # 3
print(sum_all(1, 2, 3, 4)) # 10

# args 是元组，可以迭代
print(sum_all(*[1, 2, 3])) # 6（使用 * 解包列表）
```

### 3.5 关键字可变参数 **kwargs

```python
# **kwargs 接收任意数量的关键字参数，以字典形式传入
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=25, city="Beijing")
# name: Alice
# age: 25
# city: Beijing

# 使用字典解包
info = {"name": "Bob", "age": 30}
print_info(**info)
```

### 3.6 参数顺序总结

```python
def func(pos1, pos2, default="value", *args, **kwargs):
    print(f"位置参数: {pos1}, {pos2}")
    print(f"默认参数: {default}")
    print(f"可变位置参数: {args}")
    print(f"可变关键字参数: {kwargs}")

func(1, 2)
# 位置参数: 1, 2
# 默认参数: value
# 可变位置参数: ()
# 可变关键字参数: {}

func(1, 2, "custom", 4, 5, name="Alice", age=25)
# 位置参数: 1, 2
# 默认参数: custom
# 可变位置参数: (4, 5)
# 可变关键字参数: {'name': 'Alice', 'age': 25}
```

---

## 四、返回值

### 4.1 返回单个值

```python
def square(x):
    return x ** 2

print(square(5))   # 25
```

### 4.2 返回多个值

```python
def get_min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = get_min_max([3, 1, 4, 1, 5, 9])
print(minimum, maximum)   # 1 9

# 实际上是返回元组
result = get_min_max([3, 1, 4])
print(result)             # (1, 4)
print(type(result))       # <class 'tuple'>
```

### 4.3 提前返回

```python
def divide(a, b):
    if b == 0:
        return None   # 提前返回
    return a / b

print(divide(10, 2))   # 5.0
print(divide(10, 0))   # None
```

---

## 五、文档字符串

```python
def calculate_bmi(weight, height):
    """
    计算 BMI 指数

    参数:
        weight (float): 体重，单位千克
        height (float): 身高，单位米

    返回:
        float: BMI 值

    示例:
        >>> calculate_bmi(70, 1.75)
        22.857142857142858
    """
    return weight / (height ** 2)

# 查看文档
print(calculate_bmi.__doc__)

# help() 查看帮助
help(calculate_bmi)
```

---

## 六、综合示例

```python
"""
简易计算器函数库
"""

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):
    """除法，除数为 0 时返回 None"""
    if b == 0:
        print("错误：除数不能为 0")
        return None
    return a / b

def calculator(expression):
    """
    解析并计算简单的四则运算表达式
    示例: "10 + 5", "20 * 3"
    """
    parts = expression.split()
    if len(parts) != 3:
        return None

    a = float(parts[0])
    op = parts[1]
    b = float(parts[2])

    operations = {
        "+": add,
        "-": subtract,
        "*": multiply,
        "/": divide,
    }

    if op in operations:
        return operations[op](a, b)
    return None


# 测试
print(calculator("10 + 5"))    # 15.0
print(calculator("20 * 3"))    # 60.0
print(calculator("15 / 3"))    # 5.0
print(calculator("10 / 0"))    # 错误：除数不能为 0
```

---

## 小结

- 使用 `def` 定义函数，`return` 返回值
- 参数类型：位置参数、关键字参数、默认参数、`*args`、`**kwargs`
- 默认参数使用不可变对象，如需可变对象用 `None` 做默认值
- 函数可以返回多个值（实际是元组）
- 使用文档字符串说明函数功能，可通过 `__doc__` 或 `help()` 查看

---

## 练习

1. 编写一个函数 `is_prime(n)`，判断一个数是否为质数。
2. 编写一个函数 `factorial(n)`，计算阶乘（使用递归或循环）。
3. 编写一个函数，接收任意数量的数字参数，返回它们的平均值、最大值和最小值。
4. 编写一个函数 `safe_divide(a, b, default=0)`，当除数为 0 时返回 default 值。
5. 为以下函数编写文档字符串：
   ```python
   def celsius_to_fahrenheit(c):
       return c * 9 / 5 + 32
   ```

