深入剖析 Python 函数参数传递机制及高级应用

前言

在本篇文章中,笔者将带你深入探讨 Python 函数传参的进阶主题。
通过阅读本篇文章,你可以深入了解 Python 函数传参的进阶主题,掌握更多高级的函数技巧,提升你的 Python 编程能力。

前面分享了Python 函数传参基础篇:《提升Python函数调用灵活性:参数传递类型详解

结合食用,效果更佳。

实现⚡⚡

参数的作用域

注意事项:

  • 使用global关键字。将函数内部的 变量 声明为全局变量,使得在函数内部修改的值也能影响到函数外部。

在这个例子中,x 在函数 func 内部是一个局部变量,它的作用范围仅限于函数内部。

而在函数外部定义的 x 是一个全局变量,可以在函数内部和外部访问。

def func():
    x = 10  # 局部变量
    print("Inside func:", x)


x = 5  # 全局变量
func()
print("Outside func:", x)

输出:

Inside func: 10
Outside func: 5

如果需要改变函数外部的值,通过使用 global 关键字,将函数内部的 x 声明为全局变量,使得在函数内部修改的值也能影响到函数外部。

def func():
    global x
    x = 10  # 局部变量
    print("Inside func:", x)


x = 5  # 全局变量
func()
print("Outside func:", x)

输出:

Inside func: 10
Outside func: 10

参数的传递方式

注意事项:

  • 避免在函数内部修改可变对象:在函数内部修改可变对象这是不建议的。
  • 如果不希望函数内部的操作影响到外部变量,可以在函数内部创建一个新的可变对象,并对其进行操作,而不是直接修改传入的可变对象。
  • 需要注意函数参数传递时对象的可变性,了解不可变对象和可变对象在函数内外的表现,并根据需要采取适当的操作来避免意外修改。

理解传值传递传引用传递的区别:

  • 传值传递是将对象的副本传递给函数,函数对副本的修改不会影响到原始对象;
  • 传引用传递是将对象的引用传递给函数,函数对引用所指向的对象的修改会影响到原始对象。

这是理解参数传递方式的关键,可以帮助我们正确处理函数内外的变量关系。




在这个例子中,a 是一个不可变对象(整数),而 b 是一个可变对象(列表)。

当将它们作为参数传递给函数时,

  • 不可变对象(如整数、字符串、元组) 是通过传值传递的,所以在函数内部对 a 的修改不会影响外部变量 x

  • 可变对象(如列表、字典) 是通过传引用传递的,所以在函数内部对 b 的修改会影响外部变量 y

def modify_value(a, b):
    a = 10
    b.append(4)


x = 5
y = [1, 2, 3]
modify_value(x, y)
print(x)  # 输出: 5
print(y)  # 输出: [1, 2, 3, 4]

匿名函数和 Lambda 表达式

注意事项:

  • lambda 关键字用于定义匿名函数:lambda 关键字用于创建一个匿名函数,也称为 lambda 表达式。它可以在需要一个简单的函数定义的地方使用,并且通常用于一次性的、简单的函数需求。
  • lambda 表达式强调了函数定义的简洁性和方便性。它适用于一些简单的函数需求,可以避免编写完整的函数定义和命名的过程,使代码更加简洁和易读。
  • 匿名函数常见于函数式编程操作,如排序、过滤和映射等。它们可以作为参数传递给其他函数,使代码更加灵活和可扩展。
  • 尽管 lambda 表达式可以提供一种简洁的函数定义方式,但请注意适用场景。对于复杂的逻辑和需要复用的函数,建议使用普通函数进行定义,以提高代码的可读性和维护性。

Python lambda用法:

lambda arguments: expression

示例 1:对数字进行相加

在这个例子中,lambda 关键字用于定义一个匿名函数,它接受两个参数 xy,并返回它们的乘积。

使用 lambda 表达式可以简洁地定义简单的函数。

add_numbers = lambda x, y: x + y
result = add_numbers(5, 3)
print("Result:", result)	# 输出: 8

示例 2:对列表进行升序排序

在这个示例中,sorted() 函数使用了 key 参数,该参数接受一个函数作为排序的依据。

通过 lambda 表达式 lambda x: x,我们指定以列表中的每个元素作为排序依据。因此,列表会按照元素的大小进行升序排序。

numbers = [5, 2, 8, 1, 9]
sorted_numbers = sorted(numbers, key=lambda x: x)
print(sorted_numbers)  # 输出: [1, 2, 5, 8, 9]

示例 3:对字典列表按照字典键进行排序

在这个示例中,有一个字典列表 persons,其中每个字典包含一个名字和年龄。

使用 sorted() 函数和 key 参数,将 lambda 表达式 lambda x: x["name"] 作为排序依据。这样,字典列表将按照字典的 "name" 键进行升序排序。

persons = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30},
    {"name": "Charlie", "age": 20}
]

sorted_persons = sorted(persons, key=lambda x: x["name"])
print(sorted_persons)
# 输出: [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}, {'name': 'Charlie', 'age': 20}]

装饰器

装饰器可以实现很多有趣的功能,这里只对基础使用做一下介绍。

注意事项:

  • 确保装饰器函数的返回值是一个函数:装饰器函数必须返回一个函数对象,以便能够正确地替代原始函数。
  • 装饰器的作用是修改函数的行为:装饰器的目的是在不修改原始函数源代码的情况下,添加一些额外的功能或修改函数的行为。
  • 可以链式应用多个装饰器:Python 允许链式应用多个装饰器,即在函数定义前使用多个装饰器语句。装饰器的应用顺序与装饰器语句的顺序相反,最后应用的装饰器最先执行。
  • 装饰器的顺序是从上到下的。

示例一:将字母转换为大写装饰器

在这个例子中,uppercase_decorator 是一个装饰器函数,它接受一个函数作为参数,并返回一个新的函数。

这个装饰器函数将原始函数的返回值转换为大写形式。通过在函数定义前使用 @ 符号应用装饰器,可以直接将装饰器应用于函数。

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()

    return wrapper


@uppercase_decorator
def demo(name):
    return "Hello, " + name


result = demo("Frica")
print(result)  # 输出: HELLO, Frica

示例二:将字母转换为大写装饰器并添加标签

在示例中,demo 函数先应用 uppercase_decorator,然后应用 bold_decorator

最终的执行顺序是先执行 bold_decorator 的包装函数,然后执行 uppercase_decorator 的包装函数。最后,调用 demo("Frica") 会返回 HELLO, FRICA

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()

    return wrapper


def bold_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return "" + result + ""

    return wrapper


@bold_decorator
@uppercase_decorator
def demo(name):
    return "Hello, " + name


result = demo("Frica")
print(result)  # 输出: HELLO, FRICA

参数的打包和解包

注意事项:

  • 打包:通过使用 *** 运算符,可以将传递给函数的参数打包成一个元组或字典;
  • 解包:通过使用 *** 运算符,可以将元组或字典中的值解构为单独的参数,并将它们传递给函数;
  • 在函数调用时,使用 * 运算符将元组中的元素分别传递给函数的位置参数;
  • 在函数调用时,使用**运算符将字典的键值对打包并传递给函数的关键字参数;
  • 通过参数解包,可以轻松地将可迭代对象的元素传递给函数,而无需一个个地指定参数的值。这种方式提供了更灵活和方便的函数调用方式,并使代码更加简洁易读。

在这个例子中,datainfo 是元组和字典,通过解构操作符 *** 将它们的值解构为单独的参数。

paramsdetails 是普通的参数值,通过打包操作符 *** 将它们的值打包成元组或字典作为参数传递给函数。

def demo(name, age):
    print(f"Name: {name},", f"Age: {age}")


# 参数解包
data = ("Frica", 25)
demo(*data)  # 输出: Name: Frica, Age: 25

# 参数解包
info = {"name": "dd", "age": 30}
demo(**info)  # 输出: Name: dd, Age: 30

参数类型注解

这个在上一篇文章中有介绍,无论何时,为你的函数和参数添加类型注解,这是很有必要的。

作用如下:

  • 提供类型提示:类型注解可以指定函数参数和返回值的预期类型,从而提供给开发者更明确的类型提示。这有助于开发者理解函数的输入和输出,并在代码编写过程中提供自动补全和错误检查的功能。
  • 增加代码可读性:类型注解可以提高代码的可读性,使代码更加清晰易懂。通过类型注解,开发者可以清楚地了解函数参数的期望类型,从而更好地理解函数的作用和用法。
  • 增强代码健壮性:类型注解可以在编译时或运行时进行类型检查,从而捕获潜在的类型错误。通过类型检查,可以避免在函数调用时传递错误类型的参数,提前发现并修复类型相关的问题,提高代码的健壮性和可靠性。
  • 提升代码维护性:类型注解可以帮助开发团队更好地理解代码,并减少对代码的误解。在代码维护和重构过程中,类型注解可以作为重要的参考信息,帮助开发者正确修改和调整函数的使用方式。
  • 支持静态类型检查工具:添加类型注解可以支持使用静态类型检查工具,例如 MyPy、Pyright 等。这些工具可以对代码进行静态分析,发现类型相关的问题,并提供进一步的建议和修复方案。

需要注意的是,类型注解并不会影响代码的实际执行,它们只是提供给开发者和工具使用的信息。

在 Python 中,类型注解是可选的,并且不会导致运行时错误。但是,类型注解可以在代码开发和维护过程中提供很多有益的帮助,

def demo(name: str, age: int) -> str:
    return "Hello, " + name + "! You are " + str(age) + " years old."


print(demo("Frica", 25))	# 输出: Hello, Frica! You are 25 years old.

总结✨✨

本篇文章深入介绍了Python中函数传参机制的高级应用。

  • 参数的作用域:介绍了局部作用域、全局作用域和嵌套作用域的概念及其关系。这有助于理解在不同作用域中如何访问和操作变量。

  • 参数的传递方式:探讨了传值调用和传引用调用的区别,以及可变对象和不可变对象的概念。这对于理解函数参数的传递机制非常重要。

  • 匿名函数和 Lambda 表达式:介绍了匿名函数的定义和使用,以及 Lambda 表达式的语法和应用场景。这些技巧可以简化代码并提高可读性。

  • 装饰器:解释了装饰器的作用和用法,包括如何定义和应用装饰器来增强函数的功能。装饰器是 Python 中强大的函数修饰工具。

  • 参数的解构和打包:讨论了参数解构和打包的操作符 *** 的使用方法,以及如何处理多个参数的情况。这些技巧可以增加代码的灵活性。

  • 参数类型注解:介绍了参数类型注解的概念和用法,以及如何在函数定义时指定参数的类型。这有助于提高代码的可读性和可维护性。

总体上,这些主题涵盖了函数传参的关键概念和技巧,适合进阶的 Python 程序员学习和掌握。

后话

本次分享到此结束,
see you~~‍‍

你可能感兴趣的:(Python,python,java,前端)