函数_可变长参数_名称空间_作用域

发布时间 2023-12-11 15:01:18作者: Unlucky

【一】函数

函数是一段可重复使用的代码块,用于执行特定的任务或操作。它可以接受输入参数并返回输出结果。函数的作用是将一组相关的代码组织在一起,并通过函数名来调用执行。

在Python中,函数可以帮助我们实现以下几个方面的功能:

  1. 代码重用:通过定义函数,可以将一段代码片段封装起来,使其可以在程序的不同位置重复使用,提高代码的可维护性和复用性。

  2. 模块化编程:将复杂的问题分解为多个函数,每个函数负责解决一个子问题,从而降低代码的复杂度,使程序更易于理解和维护。

  3. 提高代码可读性:通过函数的定义和命名,可以使代码更加清晰易懂,增加代码的可读性和可理解性。

  4. 参数传递:函数可以接受输入参数,通过参数的传递,可以将外部数据传递给函数内部进行处理,并返回处理结果。参数可以是必需参数、默认参数、可变参数或关键字参数,提供了灵活的参数传递方式。

  5. 封装复杂操作:函数可以将一系列复杂的操作封装为一个单独的函数,使代码更加简洁和可管理。这有助于提高代码的可维护性和可测试性。

  6. 代码组织和管理:通过函数的使用,可以将代码按功能模块进行组织和管理,使得代码结构更清晰,易于阅读和维护。

总之,函数是编程中非常重要的概念,它提供了一种结构化的方式来组织和管理代码,并使代码更加灵活、可重用和可扩展。

1.1 - 函数与定义

  1. 定义函数:使用关键字def后跟函数名,然后在括号内指定函数的参数列表,最后以冒号结尾。在冒号后,缩进的代码块表示函数体,即函数的具体实现。

    例如,定义一个简单的函数来打印"Hello, World!"的消息:

def say_hello():  
    print("Hello, World!")
  1. 调用函数:通过函数名后跟括号来调用函数。如果函数有参数,则在括号内传递相应的参数值。

例如,调用上面定义的函数:

say_hello()

这将输出"Hello, World!"的消息。

1.2 - 函数参数传递

  1. 无参无返回值
def say_hello():  
    print("Hello, World!")

say_hello()
  1. 有参无返回值
def say_hello(name):  
    print(f"Who am i, {name}")  
  
  
say_hello(name='???')  # Who am i, ???
  1. 多参无返回值
def say_hello(name1, name2):  
    print(f"Who's my favorite ? {name1}, I think is {name2}")  
  
  
say_hello(name1='ash', name2='anna')  # Who's my favorite ? ash, I think is anna
  1. 多参一返回值
def num_sum(num1, num2):  
    sum1 = num1 ** num2  
    return sum1  # 4  
  
  
res = num_sum(2, 2)  
print(res)  # 4
  1. 多参多返回值
def get_user_info():  
    username_input = input('请输入用户名').strip()  
    password_input = input('请输入密码').strip()  
  
    return username_input, password_input  
  
  
username, passwd = get_user_info()  
print(f'用户名是:{username},密码是:{passwd}')  # 用户名是:wx,密码是:123

1.3 - 函数的三种调用方式

  1. 函数名直接调用
def say_hello():  
    print("Hello, World!")

say_hello()
  1. 表达式调用
def num_sum(num1, num2):  
    sum1 = num1 ** num2  
    return sum1  # 4  
  
  
new_func = num_sum  
res = num_sum(2, 2)  
print(res)  # 4 
  1. 函数作为参数调用
def fun1(num1, num2):  
    return num1 * num2  
  
  
def fun2(func_name, num1, num2):  
    return func_name(num1, num2)  
  
  
res = fun2(fun1, 2, 2)  
print(res)  # 4

1.4 - 实际参数与形式参数

实参(实际参数)和形参(形式参数)是在函数定义和函数调用中使用的术语。

形参是函数定义中用来接收参数值的占位符。它们是在函数定义时指定的,用于定义函数的输入。形参是函数内部使用的局部变量,它们的作用域仅限于函数内部。

实参是函数调用时传递给函数的具体数值或对象。它们是在函数调用时提供的,用于向函数传递数据。实参的值被赋给函数定义中对应的形参,函数在执行时可以使用这些实参值进行计算和处理。

形参和实参之间的关系是通过函数调用来建立的。当调用函数时,将实参传递给函数,函数使用这些实参来执行其功能。实参的值被复制到形参中,函数在执行过程中使用形参来处理数据。

形参和实参的个数、类型和顺序必须匹配,否则会导致错误。在函数调用时,可以按照形参的顺序依次传递实参值,也可以通过关键字参数的方式指定传递的实参值。关键字参数允许根据参数名来指定实参值,不需要按照形参的顺序进行传递。

下面是一个简单的示例,展示了函数定义和函数调用中形参和实参的使用:

def add_numbers(x, y):  # x和y是形参  
    sum1 = x + y  
    print("Sum:", sum1)  
    return sum1  
  
  
# 实参是常量  
add_numbers(3, 5)  # 3和5是实参  # Sum: 8  

# 实参是变量  
x = 10  
y = 5  
add_numbers(x, y)  # Sum: 15  
  
# 实参是表达式  
res = add_numbers(2 ** 2 + 16, 10 * add_numbers(5, 5))  # Sum: 120

1.4.1 关键字参数

  1. 变量传参
def func1(name, gender, height):  
    print(f'姓名:{name},性别:{gender},身高:{height}')  
    # 姓名:wx,性别:male,身高:180  
  
  
func1(height='180', name='wx', gender='male')

1.4.2 位置参数

  1. 按顺序传参
def func1(name, gender, height):  
    print(f'姓名:{name},性别:{gender},身高:{height}')  
    # 姓名:wx,性别:male,身高:180  
  
  
func1('wx', 'male', '180')

1.4.3 默认参数

  1. 不赋值变量使用默认参数
def func1(name, gender, height, addr='sh'):  
    print(f'姓名:{name},性别:{gender},身高:{height},地址:{addr}')  
    # 姓名:wx,性别:male,身高:180,地址:sh  
  
  
func1(height='180', name='wx', gender='male')  # 不给addr变量赋值默认使用sh

1.5 - 可变长参数

1.5.1 *args

_args的作用是允许函数接受不定数量的位置参数,并将这些参数作为一个元组(tuple)传递给函数。在函数定义时,可以使用_args来表示可变长的位置参数。

使用*args的好处是可以在调用函数时传递任意数量的位置参数,而不需要提前定义形式参数的个数。这样可以增加函数的灵活性,使其能够适应不同数量的参数。

在函数内部,可以使用args来处理传入的位置参数。args是一个元组,可以通过索引访问其中的参数值。

以下是一个示例,展示了如何使用*args:

def example_func(*args):
    for arg in args:
        print(arg)

example_func(1, 2, 3, 4)

输出结果为:
1
2
3
4

1.5.2 **kwargs

kwargs的作用是允许函数接受不定数量的关键字参数,并将这些参数作为一个字典(dictionary)传递给函数。在函数定义时,可以使用**kwargs来表示可变长的关键字参数。

使用kwargs的好处是可以在调用函数时传递任意数量的关键字参数,而不需要提前定义形式参数的个数。这样可以增加函数的灵活性,使其能够适应不同的关键字参数。

在函数内部,可以使用kwargs来处理传入的关键字参数。kwargs是一个字典,可以通过键值对的方式访问其中的参数值。

以下是一个示例,展示了如何使用**kwargs:

def example_func(**kwargs):
    for key, value in kwargs.items():
        print(key, value)

example_func(name="Alice", age=25, city="New York")

输出结果为:
name Alice
age 25
city New York

1.6 - 名称空间

名称空间(Namespace)在Python中是用来存储和查找变量名称的一种机制。它可以看作是一个变量名到对象的映射。

Python中有多个不同的名称空间,包括内置名称空间(built-in namespace)、全局名称空间(global namespace)和局部名称空间(local namespace)。每个名称空间都有不同的作用范围和生命周期。

  • 内置名称空间(built-in namespace):包含了Python解释器中内置的函数和对象,比如print()len()等。这些名称在任何地方都可以直接使用,无需导入模块或声明变量。

  • 全局名称空间(global namespace):属于整个程序的全局范围,包括在全局作用域中定义的变量和函数。全局名称空间可以在整个程序中的任何地方访问,但需要使用global关键字声明全局变量。

  • 局部名称空间(local namespace):属于函数或类等定义体内部的范围,包含在函数内部定义的变量和函数参数。局部名称空间只在函数执行期间存在,函数调用结束后会被销毁。

名称空间的作用是提供变量名的唯一性和隔离性,避免命名冲突和混淆。不同的名称空间中可以有相同的变量名,它们互不影响。当在代码中使用一个变量名时,Python会按照一定的规则从内置、全局和局部名称空间中查找对应的变量。

1.6.1 名称空间的变量查找顺序

  1. 在命名空间中,可以通过作用域规则来管理变量的可见性。Python按照一定的规则从内置、全局和局部命名空间中查找变量。具体的作用域规则如下:

    • 当在函数内部使用一个变量时,Python首先在局部命名空间中查找该变量,如果找到则使用局部变量。
    • 如果在局部命名空间中找不到变量,Python会继续在全局命名空间中查找,如果找到则使用全局变量。
    • 如果在全局命名空间中也找不到变量,Python会最后在内置命名空间中查找,如果找到则使用内置变量。

    这种作用域规则确保了变量在不同的命名空间中有不同的可见性,从而实现了变量的隔离和作用域管理。

1.7 - 作用域

作用域(Scope)在编程中用于确定变量的可见性和生命周期。它规定了在程序的不同部分中哪些变量可以被访问和使用。

作用域的作用有以下几个方面:

  1. 变量的可见性:作用域确定了变量在代码中的可见范围。在一个作用域内定义的变量可以在同一作用域以及嵌套的子作用域中被访问和使用,而在其他作用域中是不可见的。这样可以限制变量的访问范围,避免命名冲突和混淆。

  2. 变量的生命周期:作用域还决定了变量的生命周期,即变量在内存中的存在时间。在进入一个作用域时,相关的变量被创建,当作用域结束时,这些变量被销毁并释放内存资源。这样可以有效地管理内存,避免不必要的资源占用。

  3. 作用域链:作用域之间可以形成一个嵌套的层级关系,称为作用域链。作用域链的顶端是全局作用域,最底端是当前代码块的作用域。当在一个作用域中访问一个变量时,Python会按照作用域链的顺序从内层向外层逐级查找,直到找到该变量或者抵达全局作用域。这样可以实现变量的层级查找和继承。

作用域的概念有助于组织和管理变量,提高代码的可读性、可维护性和可重用性。合理使用作用域可以避免命名冲突,控制变量的可见性,同时也有利于优化内存和资源的使用。

1.7.1 global

global关键字在Python中用于声明一个变量为全局变量。当在函数内部使用global关键字声明一个变量时,该变量将被视为全局变量,即使在函数内部对该变量进行赋值,也会影响到全局作用域中的变量。

使用global关键字的作用有以下几个方面:

  1. 修改全局变量:在函数内部使用global关键字声明一个变量后,可以在函数内部修改该变量的值,并且这个修改会在函数执行完毕后反映到全局作用域中。否则,在函数内部如果直接对一个变量进行赋值,Python会创建一个新的局部变量,而不会影响到全局作用域中的同名变量。

  2. 访问全局变量:在函数内部可以通过global关键字访问全局作用域中的变量。如果在函数内部没有使用global关键字声明一个变量,而直接使用该变量,Python会在函数内部创建一个同名的局部变量,该变量的值与全局变量的值可能不同。

def get_user_info():  
    global username_input, password_input  
    username_input = input('请输入用户名').strip()  
    password_input = input('请输入密码').strip()  
    return username_input, password_input

1.7.2 nonlocal

def outer_function():
    x = 10

    def inner_function():
        nonlocal x
        x = 20
        print("Inside the inner function:", x)

    inner_function()
    print("Inside the outer function:", x)

outer_function()

运行这段代码会输出以下结果:

Inside the inner function: 20
Inside the outer function: 20

PS:内部函数可以修改的局部变量类型是基本数据类型(如整数、浮点数、布尔值等)以及可变类型(如列表、字典等)。这是因为基本数据类型和可变类型在函数内部被视为可变对象,可以通过nonlocal关键字进行修改。

然而,对于不可变类型(如元组、字符串等),尽管可以在内部函数中访问它们,但无法通过nonlocal关键字进行修改。

1.7.3 global和nonlocal的区别

  1. global 关键字:用于在函数内部访问和修改全局变量。通过在函数内部使用 global 关键字声明变量,可以告诉解释器该变量是全局作用域的,从而可以在函数内部访问和修改它。在函数内部修改全局变量时,不需要返回修改后的值,因为对全局变量的修改会直接影响到整个程序中对该变量的访问。

  2. nonlocal 关键字:用于在嵌套函数内部访问和修改非局部变量。当在一个函数内部定义了另一个函数时,内部函数可以访问外部函数的变量。但是,默认情况下,内部函数只能访问外部函数的变量,而不能修改它们。如果需要在内部函数中修改外部函数的变量,可以使用 nonlocal 关键字声明变量,从而告诉解释器该变量是非局部的。使用 nonlocal 关键字可以在内部函数中访问和修改外部函数的变量,而不是创建一个新的局部变量。