• Python进阶系列 - 17讲 函数


    在这篇文章中我们将详细讲讲函数参数相关知识。

    我们将学习一下内容:

    • 实参和参数的区别
    • 位置和关键字参数
    • 默认参数
    • 可变长度参数(*args**kwargs
    • 容器解包成函数参数
    • 本地与全局参数
    • 参数传递(通过值还是通过引用?)

    实参和参数

    • 参数是定义函数时在括号内定义或使用的变量
    • 实参是调用函数时为这些参数传递的值

    代码:

    def print_name(name): # name 是参数
        print(name)
    print_name('Alex') # 'Alex' 是实参
    
    • 1
    • 2
    • 3

    结果:

    Alex
    
    • 1

    位置和关键字参数

    我们可以将参数作为位置参数或关键字参数传递。

    关键字参数的一些好处可以是:

    • 我们可以通过它们的名字来调用参数,以使其更清楚它们代表什么
    • 我们可以重新排列参数位置,使它们最易读

    代码:

    def foo(a, b, c):
        print(a, b, c)
    
    # 位置参数
    foo(1, 2, 3)
    # 关键字实参
    foo(a=1, b=2, c=3)
    foo(c=3, b=2, a=1) # 可以打乱顺序
    
    # 混合使用
    foo(1, b=2, c=3)
    # 混合使用时,关键字实参必须出现在位置实参之前
    # foo(1, b=2, 3) # ❌
    # foo(1, b=2, a=3) # ❌ 冲突
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    结果:

    1 2 3
    1 2 3
    1 2 3
    1 2 3
    
    • 1
    • 2
    • 3
    • 4

    默认参数

    函数可以有带有预定义值的默认参数。
    该参数可以省略,然后将默认值传递给函数,或者该参数可以与不同的值一起使用。
    请注意,默认参数必须定义在非默认参数之后。

    代码:

    def foo(a, b, c, d=4):
        print(a, b, c, d)
    
    
    foo(1, 2, 3, 4)
    foo(1, b=2, c=3, d=100)
    
    # ❌ 不允许默认参数在非默认参数之前
    # def foo(a, b=2, c, d=4):
    #     print(a, b, c, d)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果:

    1 2 3 4
    1 2 3 100
    
    • 1
    • 2

    可变参数(*args**kwargs

    • 如果用一个星号 (*) 标记参数,则可以将任意数量的位置参数传递给函数(通常称为 *args)。
    • 如果用两个星号 (**) 标记参数,则可以将任意数量的关键字参数传递给此函数(通常称为 **kwargs)。

    代码:

    def foo(a, b, *args, **kwargs):
        print(a, b)
        for arg in args:
            print(arg)
        for kwarg in kwargs:
            print(kwarg, kwargs[kwarg])
    
    
    # 1,2:位置参数,3,4,5:args参数,6,7:kwargs参数
    foo(1, 2, 3, 4, 5, six=6, seven=7)
    print()
    # *args和**kwargs是可变的,也可以省略。
    foo(1, 2, three=3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    结果:

    1 2
    3
    4
    5
    six 6
    seven 7
    1 2
    three 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    强制关键字参数

    有时您希望一些参数必须是关键字参数,不能是位置参数。

    您可以通过两种方法实现:

    • 在函数参数列表中写入’*',则之后的所有参数都必须作为关键字参数传递。

    • 在可变参数后面定义参数一定是关键字参数。

    代码:

    # ✅方法一: 用*号分割
    def foo(a, b, *, c, d):
        print(a, b, c, d)
    
    
    foo(1, 2, c=3, d=4)
    # foo(1, 2, 3, 4) # ❌:c,d是不可省略的关键字参数
    
    # ✅方法二: 在*args或**kwargs之后定义
    def foo(*args, last):
        for arg in args:
            print(arg)
        print(last)
    foo(8, 9, 10, last=50)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    结果:

    1 2 3 4
    8
    9
    10
    50
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参数解包

    • 如果列表的长度与函数参数的数量相匹配,则可以将列表或元组解压缩为带有一个星号 (*) 的参数。
    • 同理,字典可以解压成带有两个星号(**)的参数,长度和键与函数参数匹配。

    代码:

    def foo(a, b, c):
        print(a, b, c)
    
    
    my_list = [4, 5, 6]  # 列表或者元组
    foo(*my_list)  # 参数拆包
    
    my_dict = {"a": 1, "b": 2, "c": 3}  # 字典
    foo(**my_dict)
    
    # my_dict = {'a': 1, 'b': 2, 'd': 3} # ❌ 关键字参数不匹配
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果:

    4 5 6
    1 2 3
    
    • 1
    • 2

    局部变量和全局变量

    Global variables can be accessed within a function body, but to modify them,
    we first must state global var_name in order to change the global variable.

    代码:

    def foo1():
        print("打印全局变量:", number)  # number函数内没有定义,生成一个局部变量,值和全局变量一样
        # number = 1 # ❌ 不允许修改未声明的全局变量
    
    def foo2():
        global number  # 声明全局变量,修改才有效
        number = 3
        print("打印函数内全局变量:", number)
    
    number = 0
    print("打印全局变量:", number)
    foo1()
    print("执行完foo1函数后,打印全局变量:", number)
    
    foo2()  # 修改全局变量
    print("执行完foo2函数后,打印全局变量:", number)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果:

    打印全局变量: 0
    打印全局变量: 0
    执行完foo1函数后,打印全局变量: 0
    打印函数内全局变量: 3
    执行完foo2函数后,打印全局变量: 3
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们不写 global var_name 并为与全局变量同名的变量分配一个新值,
    这将在函数内创建一个局部变量。
    全局变量保持不变。

    参数传递

    Python 使用一种称为“Call-by-Object”或“Call-by-Object-Reference”的机制。
    必须考虑以下规则:

    • 传入的参数其实是一个对象的引用(但是引用是按值传递的)
    • 可变和不可变数据类型之间的区别

    这意味着:

    1. 可变对象(例如列表、字典)可以在方法内更改。
    2. 但是如果在方法中重新绑定引用,外部引用仍然会指向原来的对象。
    3. 不可变对象(例如 int、string)不能在方法内更改。
    4. 但是可变对象中包含的不可变对象可以在方法中重新分配。

    1. 不可变对象(例如 int、string)不能在方法内更改。

    代码:

    def foo(x):
        x = 5  # x是不可变对象。
    var = 10
    print(f"函数执行前:var={var}")
    foo(var)  # 执行foo函数,参数var会被更改为5
    print(f"函数执行后:var={var}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:

    函数执行前:var=10
    函数执行后:var=10
    
    • 1
    • 2

    2. 可变对象(例如列表、字典)可以在方法内更改。

    代码:

    def foo(列表):
        列表.append(4)
    A = [1, 2, 3]
    print(f'函数执行前: A = {A}')
    foo(A)
    print(f'函数执行后: A = {A}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:

    函数执行前: A = [1, 2, 3]
    函数执行后: A = [1, 2, 3, 4]
    
    • 1
    • 2

    3. 可变对象中包含的不可变对象可以在方法中重新分配。

    代码:

    def foo(a_list):
        a_list[0] = -100
        a_list[2] = "Paul"
    
    my_list = [1, 2, "Max"]
    print(f'函数执行前: my_list = {my_list}')
    foo(my_list)
    print(f'函数执行后: my_list = {my_list}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果:

    函数执行前: my_list = [1, 2, 'Max']
    函数执行后: my_list = [-100, 2, 'Paul']
    
    • 1
    • 2

    4. 参数传递的是对象的引用。

    函数内,重新绑定引用,外部引用仍然会指向原来的对象。

    代码:

    def foo(a_list):
        a_list = [50, 60, 70] # 重新定义了a_list,为局部变量。
        a_list.append(50)
    my_list = [1, 2, 3]
    print(f'函数执行前: my_list = {my_list}')
    foo(my_list)
    print(f'函数执行后: my_list = {my_list}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结果:

    函数执行前: my_list = [1, 2, 3]
    函数执行后: my_list = [1, 2, 3]
    
    • 1
    • 2

    小心使用可变类型的 +== 操作。
    +=不会重新定义变量,而=会重新定义变量。
    也就是说,+=是对变量进行追加,而=是重新定义变量。

    代码:

    def foo(a_list):
        a_list += [4, 5] #⚠️ 改变了传入的参数,不是局部变量。
    def bar(a_list):
        a_list = a_list + [4, 5] #⚠️ 重新定义了传入的参数,是局部变量。
    
    my_list = [1, 2, 3]
    print(f'foo函数执行前: my_list = {my_list}')
    foo(my_list)
    print(f'foo函数执行后: my_list = {my_list}')
    my_list = [1, 2, 3]
    print(f'bar函数执行前: my_list = {my_list}')
    bar(my_list)
    print(f'bar函数执行后: my_list = {my_list}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    结果:

    foo函数执行前: my_list = [1, 2, 3]
    foo函数执行后: my_list = [1, 2, 3, 4, 5]
    bar函数执行前: my_list = [1, 2, 3]
    bar函数执行后: my_list = [1, 2, 3]
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    DPDK-A3: KVM使用SRIOV和虚机使用DPDK
    Linux系统之file命令的基本使用
    自动化机械臂喷涂生产线方案五
    ceph 1 pool(s) do not have an application enabled
    基于springboot“漫画之家”系统设计与实现-计算机毕业设计源码+LW文档
    Python算法练习 9.18
    【最新】如何在CSDN个人主页左侧栏添加二维码?侧边推广怎么弄?
    [附源码]java毕业设计 宠物医院管理系统
    Class.forName() 与 ClassLoader.loadClass() 的区别
    运维困局下确保系统稳定的可行性
  • 原文地址:https://blog.csdn.net/pythontip/article/details/126911330