Bootstrap

【Python 学习 / 5】函数详解(定义、参数、作用域、lambda、内置函数)

一、函数

函数是Python中组织代码的基本单位。它可以通过封装一组语句,实现代码复用。函数可以接受输入参数并返回结果,极大地提高了代码的可读性和维护性。

1. 定义函数

在Python中,使用def关键字来定义一个函数。函数名后面跟一对小括号,括号中可以放参数。函数体需要缩进。

1.1 基本函数定义

def greet():
    print("Hello, World!")

# 调用函数
greet()  # 输出: Hello, World!

1.2 带参数的函数

函数可以接受输入的参数,定义时在括号中列出参数名,调用时传入相应的值。

def greet(name):
    print(f"Hello, {name}!")

greet("Alice")  # 输出: Hello, Alice!

1.3 带返回值的函数

函数可以返回一个值,使用return语句返回结果。如果没有return语句,函数默认返回None

def add(a, b):
    return a + b

result = add(3, 4)
print(result)  # 输出: 7

2. 参数传递

Python支持多种类型的参数传递方式。函数定义时可以使用位置参数、默认参数、可变参数和关键字参数等。

2.1 位置参数

位置参数是最常见的参数传递方式,调用时必须按照定义的顺序传入值。

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

print(subtract(10, 5))  # 输出: 5

2.2 默认参数

函数定义时可以为某些参数设置默认值。调用时可以选择传入该参数的值,也可以使用默认值。

def greet(name="Guest"):
    print(f"Hello, {name}!")

greet("Alice")  # 输出: Hello, Alice!
greet()         # 输出: Hello, Guest!

2.3 可变参数

如果不确定函数需要接收多少个参数,可以使用可变参数。Python提供了两种方式来处理可变参数:

  • *args:用于接收位置参数,收集传入的多个位置参数并将它们作为元组传递。
  • **kwargs:用于接收关键字参数,收集传入的多个关键字参数并将它们作为字典传递。
2.3.1 使用*args
def sum_numbers(*args):
    return sum(args)

print(sum_numbers(1, 2, 3))  # 输出: 6
print(sum_numbers(1, 2, 3, 4, 5))  # 输出: 15
2.3.2 使用**kwargs
def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

display_info(name="Alice", age=25)  
# 输出:
# name: Alice
# age: 25

2.4 参数的混合使用

可以混合使用位置参数、默认参数、*args**kwargs,但需要遵循一定的顺序:位置参数 > 默认参数 > *args > **kwargs

def greet(name, age=30, *args, **kwargs):
    print(f"Name: {name}, Age: {age}")
    print("Additional Arguments:", args)
    print("Keyword Arguments:", kwargs)

greet("Alice", 25, "Engineer", country="USA", hobby="Reading")
# 输出:
# Name: Alice, Age: 25
# Additional Arguments: ('Engineer',)
# Keyword Arguments: {'country': 'USA', 'hobby': 'Reading'}

3. 作用域

Python中的作用域是指变量的可访问范围。作用域主要分为四种:

  • 局部作用域(Local Scope):函数内部定义的变量,只在函数内部有效。
  • 嵌套作用域(Enclosing Scope):嵌套函数中的外部函数作用域。
  • 全局作用域(Global Scope):在模块级别定义的变量,可以在整个模块内访问。
  • 内置作用域(Built-in Scope):Python内置的标识符,如print()len()等。

Python根据作用域规则来查找变量,遵循LEGB(Local -> Enclosing -> Global -> Built-in)规则。

3.1 局部和全局变量

x = 10  # 全局变量

def example():
    x = 5  # 局部变量
    print("Local x:", x)

example()  # 输出: Local x: 5
print("Global x:", x)  # 输出: Global x: 10

好的,我将详细解释globalnonlocal关键字的工作原理,并提供更详细的案例。

3.2 global 关键字

global关键字用于在函数内部声明变量为全局变量。这意味着在函数内部修改该变量的值时,修改的是全局作用域中的变量,而不是局部作用域中的新变量。

案例:修改全局变量

假设我们有一个全局变量x,我们想要在一个函数内部修改它的值。没有global关键字的话,Python会认为我们在函数内部创建了一个局部变量,而不会影响全局变量。为了确保修改全局变量,必须使用global关键字。

# 全局变量 x
x = 10

# 定义一个函数来修改 x
def modify_global():
    global x  # 声明x为全局变量
    print(f"修改前的全局变量 x: {x}")  # 输出全局变量x的值
    x = 20  # 修改全局变量 x
    print(f"修改后的全局变量 x: {x}")  # 输出修改后的值

# 调用函数
modify_global()

# 输出全局变量 x 的最终值
print(f"全局变量 x 在函数外部的值: {x}")
输出:
修改前的全局变量 x: 10
修改后的全局变量 x: 20
全局变量 x 在函数外部的值: 20

解释:

  • modify_global函数内部,通过global x声明了x是全局变量,所以我们可以修改全局作用域中的x
  • 在函数内修改x的值时,实际上修改的是全局变量,而不是在函数内创建一个新的局部变量。
  • 函数调用后,x的值已经改变,打印出的x的值为20,这就是全局作用域中变量的最新值。

3.3 nonlocal关键字

nonlocal关键字用于在嵌套函数中修改外部函数的局部变量,而不是全局变量。它会查找最靠近的外层作用域,确保修改的是外层函数的局部变量,而不是在当前函数或全局作用域中创建新变量。

案例:修改外部函数的局部变量

假设我们有一个外部函数outer,其中定义了一个变量x,并且有一个嵌套的内部函数inner。如果我们希望在inner函数中修改outer函数中的x,就需要使用nonlocal关键字。

def outer():
    x = 10  # 外部函数中的局部变量
    print(f"外部函数 outer 中的初始 x: {x}")  # 输出初始值
    def inner():
        nonlocal x  # 使用 nonlocal 修改外部函数中的 x
        print(f"内层函数 inner 中的 x(修改前): {x}")  # 输出修改前的值
        x = 20  # 修改外部函数中的 x
        print(f"内层函数 inner 中的 x(修改后): {x}")  # 输出修改后的值
    inner()  # 调用内层函数
    print(f"外部函数 outer 中的 x(修改后): {x}")  # 输出外部函数中的最终值

# 调用外部函数
outer()
输出:
外部函数 outer 中的初始 x: 10
内层函数 inner 中的 x(修改前): 10
内层函数 inner 中的 x(修改后): 20
外部函数 outer 中的 x(修改后): 20

解释:

  • outer函数中定义了变量x,初始值为10
  • inner函数中,通过nonlocal x声明xouter函数中的局部变量。这使得inner函数中的x指向outer函数的x,而不是在inner中创建一个新的局部变量。
  • inner函数中修改了x的值为20,这直接影响了外部函数outer中的x
  • 最终,outer函数中的x的值变为20

总结:

  • global:用于声明在函数内修改全局变量。
  • nonlocal:用于声明在嵌套函数中修改外部函数的局部变量,而不是创建一个新的局部变量。

4. lambda 表达式

Lambda表达式是Python中创建匿名函数的一种方式。它通常用于简化函数定义,特别是在需要传递函数作为参数的场合。

4.1 基本用法

# 定义一个简单的lambda函数
add = lambda x, y: x + y
print(add(2, 3))  # 输出: 5

4.2 与map()filter()sorted()结合使用

lambda表达式常常与Python的内置函数map()filter()sorted()一起使用,以简化代码。

  • map():将函数应用到序列的每个元素上。
  • filter():过滤掉不满足条件的元素。
  • sorted():根据指定的排序规则对序列进行排序。

# 使用lambda和map
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # 输出: [1, 4, 9, 16]

# 使用lambda和filter
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # 输出: [2, 4]

# 使用lambda和sorted
sorted_numbers = sorted(numbers, key=lambda x: -x)
print(sorted_numbers)  # 输出: [4, 3, 2, 1]

5. 常用内置函数

Python提供了大量的内置函数,这些函数可以帮助我们进行常见的操作,如数据转换、数学计算、序列操作等。

5.1 数据类型转换

  • int():将字符串或其他数字类型转换为整数。
  • float():将字符串或其他数字类型转换为浮点数。
  • str():将对象转换为字符串。
x = "123"
print(int(x))  # 输出: 123
print(float(x))  # 输出: 123.0
print(str(123))  # 输出: '123'

5.2 数学函数

  • abs():返回绝对值。
  • min():返回最小值。
  • max():返回最大值。
  • sum():返回序列中所有元素的和。
print(abs(-10))  # 输出: 10
print(min(1, 2, 3))  # 输出: 1
print(max(1, 2, 3))  # 输出: 3
print(sum([1, 2, 3]))  # 输出: 6

5.3 序列操作

  • len():返回序列的长度。
  • sorted():返回排序后的序列。
  • reversed():返回反转后的序列。
a = [3, 1, 2]
print(len(a))  # 输出: 3
print(sorted(a))  # 输出: [1, 2, 3]
print(list(reversed(a)))  # 输出: [2, 1, 3]

5.4 其他常用函数

  • enumerate():返回一个枚举对象,可以同时获取序列的索引和值。
  • zip():将多个可迭代对象打包成一个元组的列表。
  • all():如果所有元素为True,返回True
  • any():如果至少一个元素为True,返回True
a = ['apple', 'banana', 'cherry']
for idx, fruit in enumerate(a):
    print(idx, fruit)

b = [1, 2, 3]
c = ['a', 'b', 'c']
zipped = list(zip(b, c))
print(zipped)  # 输出: [(1, 'a'), (2, 'b'), (3, 'c')]

print(all([True, True, False]))  # 输出: False
print(any([False, False, True]))  # 输出: True
;