• Python的函数简介


    环境

    • Ubuntu 22.04
    • Python 3.10.4

    函数

    空函数

    def foo():
        pass
    
    
    foo()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出为空。

    其中 pass 是占位符。

    参数和返回值

    def my_max(x, y):
        return x if x > y else y
    
    
    print(my_max(10, 20))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中 x if x > y else y 是三目运算,类似于Java里的 x > y ? x : y

    结果如下:

    20
    
    • 1

    多个返回值

    def my_sum_and_avg(my_list):
        s = 0
        a = 0
        count = len(my_list)
    
        for e in my_list:
            s += e
        a = s / count
        return s, a
    
    
    list1 = [10, 20, 30, 100]
    
    result = my_sum_and_avg(list1)
    print(result)
    
    s1, a1 = my_sum_and_avg(list1)
    print(s1, a1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    结果如下:

    (160, 40.0)
    160 40.0
    
    • 1
    • 2
    • 第一次调用函数时,用一个变量接收多个返回值,会自动封装为元组;
    • 第二次调用函数时,用多个变量接收多个返回值;

    通过关键字指定参数

    前面在调用函数时,是通过位置来指定参数,此外也可以通过关键字(形参名字)来指定参数:

    def foo(width, height, age):
        print("width = ", width, "height = ", height, "age = ", age)
    
    
    foo(100, 200, 20)
    
    foo(height=100, age=30, width=200)
    
    foo(100, age=20, height=150)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:

    width =  100 height =  200 age =  20
    width =  200 height =  100 age =  30
    width =  100 height =  150 age =  20
    
    • 1
    • 2
    • 3
    • 第一次调用函数时,通过位置指定参数;
    • 第二次调用函数时,通过关键字指定参数;
    • 第三次调用函数时,位置和关键字混合使用;

    注意:位置和关键字混合使用时,关键字参数必须在位置参数之后。

    下面的用法会报错:

    foo(100, width=200, age=20)
    
    • 1

    因为第一个参数是位置参数,一定会绑定到 width 参数,如果后面再次指定 width 参数,会报错: TypeError: foo() got multiple values for argument 'width'

    参数默认值

    在定义函数时,可以给参数指定默认值。

    注意:带默认值的参数必须在没有默认值的参数之后。

    def foo(age, width=10, height=20):
        print("age = ", age, "width = ", width, "height = ", height)
    
    
    foo(20, 100, 200)
    
    foo(20)
    
    foo(20, 100)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:

    age =  20 width =  100 height =  200
    age =  20 width =  10 height =  20
    age =  20 width =  100 height =  20
    
    • 1
    • 2
    • 3
    • 第一次调用函数时,指定了三个参数;
    • 第二次调用函数时,指定了一个参数,剩余两个参数使用默认值;
    • 第三次调用函数时,指定了两个参数,最后一个参数使用默认值;

    个数可变的参数

    • *args :位置参数;
    • **kwargs 关键字参数;

    argskwargs 可以任意命名,惯例是使用 argskwargs

    def foo(*args, **kwargs):
        print("args: ", args)
        print("kwargs: ", kwargs)
        print()
    
    
    foo(1, 2, 3)
    
    foo(a=1, b=2, c=3)
    
    foo(1, 2, 3, a=4, b=5, c=6)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果如下:

    args:  (1, 2, 3)
    kwargs:  {}
    
    args:  ()
    kwargs:  {'a': 1, 'b': 2, 'c': 3}
    
    args:  (1, 2, 3)
    kwargs:  {'a': 4, 'b': 5, 'c': 6}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    逆向参数收集

    若函数需要多个参数,而调用函数时,只传一个参数(如列表,元组,字典),则会自动拆开。

    • 列表,元组:添加 *
    • 字典:添加 **
    def foo(width, height):
        print("width = ", width, "height = ", height)
    
    
    list1 = [100, 200]
    foo(*list1)
    
    tuple1 = (10, 20)
    foo(*tuple1)
    
    dict1 = {"height": 1, "width": 2}
    foo(**dict1)
    
    dict2 = dict(height=1, width=2)
    foo(**dict2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    结果如下:

    width =  100 height =  200
    width =  10 height =  20
    width =  2 height =  1
    width =  2 height =  1
    
    • 1
    • 2
    • 3
    • 4

    变量作用域

    name = "Tom"
    
    
    def foo():
        age = 20
        print(name, age)
    
    
    foo()
    
    print(name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果如下:

    Tom 20
    Tom
    
    • 1
    • 2

    没有问题,在函数内部可以访问全局变量。

    但是如果(在访问全局变量之后)试图修改其值,会报错:

    name = "Tom"
    
    
    def foo():
        print(name)
        name = "Jerry"
    
    
    foo()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    报错如下:

    UnboundLocalError: local variable 'name' referenced before assignment
    
    • 1

    这是因为在函数内部的 name = "Jerry" 会产生一个新的局部变量。

    同理,如果在函数内部只给 name 赋值:

    name = "Tom"
    
    
    def foo():
        name = "Jerry"
    
    
    print("before:", name)
    foo()
    print("after:", name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果如下:

    before: Tom
    after: Tom
    
    • 1
    • 2

    在函数内修改 name, 并不影响全局变量,这是因为函数里的 name 是一个新的局部变量,并不是那个全局的 name

    若要修改/修改全局变量,有两种方法:

    • 添加 global <变量名> 声明:
    name = "Tom"
    
    
    def foo():
        global name
        name = "Jerry"
    
    
    print("before:", name)
    foo()
    print("after:", name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果如下:

    before: Tom
    after: Jerry
    
    • 1
    • 2
    • 通过 global() 函数访问:
    name = "Tom"
    
    
    def bar():
        print(globals()['name']) # 显式使用全局变量name
        name = "Jerry" # 该name是局部变量
    
    
    print("before:", name)
    bar()
    print("before:", name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果如下:

    before: Tom
    Tom
    before: Tom
    
    • 1
    • 2
    • 3

    局部函数

    前面的函数都是在全局范围内定义的,叫做全局函数,而在函数内定义的函数叫做局部函数。

    默认情况下,局部函数对外不可见,只在其封闭(enclosing)函数内有效:

    def foo():
        def bar():
            print("hello")
        bar()
    
    
    foo()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结果如下:

    hello
    
    • 1

    foo() 函数之外是不能直接访问 bar() 函数的。

    如果局部函数需要访问其上级(全局函数)定义的变量,也有前面提到的作用域问题,需要先用 nonlocal 声明一下:

    def foo():
        name = "Tom"
        def bar():
            nonlocal name
            print(name)
            name = "Jerry"
        bar()
    
        print(name)
    
    
    foo()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    结果如下:

    Tom
    Jerry
    
    • 1
    • 2

    注意:如果没有 nonlocal name ,程序会报错 UnboundLocalError: local variable 'name' referenced before assignment

    函数变量

    函数本身也可以当做变量来使用。注意函数当变量时,后面不要加括号,否则就变成函数调用了。

    def foo():
        print("hello")
    
    
    bar = foo
    
    bar()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结果如下:

    hello
    
    • 1

    同理,函数也可以当作参数或者返回值。

    • 函数作为参数:
    def my_fun():
        print("hello")
    
    
    def foo(bar):
        bar()
    
    
    foo(my_fun)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:

    hello
    
    • 1
    • 函数作为返回值:
    def foo():
        def my_fun():
            print("hello")
    
        return my_fun
    
    
    foo()()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果如下:

    hello
    
    • 1

    foo()() 也可以写成如下形式:

    bar = foo()
    
    bar()
    
    • 1
    • 2
    • 3

    lamda表达式

    可以用lamda表达式来简化局部函数。

    • lamda表达式作为参数:
    def foo(bar):
        bar()
    
    
    foo(lambda: print("hello"))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果如下:

    hello
    
    • 1
    • lamda表达式作为返回值:
    def foo():
        return lambda: print("hello")
    
    
    foo()()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果如下:

    hello
    
    • 1

    @函数装饰器

    函数作为参数和返回值:

    def foo(fn):
        def bar(*args, **kwargs):
            print(fn.__name__, "before")
            fn(*args, **kwargs)
            print(fn.__name__, "after")
        return bar
    
    
    def myfunc1():
        print("hahaha")
    
    
    def myfunc2(a, b, c):
        print("good")
        print(a, b, c)
    
    
    foo(myfunc1)()
    
    foo(myfunc2)("aa", c="bb", b="cc")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    结果如下:

    myfunc1 before
    hahaha
    myfunc1 after
    myfunc2 before
    good
    aa cc bb
    myfunc2 after
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的写法,可用 @<函数名 的方式来简化:

    def foo(fn):
        def bar(*args, **kwargs):
            print(fn.__name__, "before")
            fn(*args, **kwargs)
            print(fn.__name__, "after")
        return bar
    
    
    @foo
    def myfunc1():
        print("hahaha")
    
    
    @foo
    def myfunc2(a, b, c):
        print("good")
        print(a, b, c)
    
    
    myfunc1()
    
    myfunc2("aa", c="bb", b="cc")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当使用 @foo 修饰 myfunc1 的函数定义时,相当于 foo(myfunc1) 。同时将 myfunc1 替换为 foo(myfunc1) 的返回值,本例中也就是 bar 函数。

    这有点类似于Spring里面的AOP。

  • 相关阅读:
    【新品发布】原来做多用途(室内、室外)无人机开发还能用它——P450进阶版
    美狐美颜SDK滤镜、哈哈镜功能算法分析
    Spring Security 中多个身份验证
    Modbus在Java中使用总结
    Python基础内容补充
    springboot监控---Spring Boot Actuator
    【C++代码】找树左下角的值,路径总和,从中序与后序遍历序列构造二叉树,从前序与中序遍历序列构造二叉树--代码随想录
    text2sql、nl2sql框架总结
    仿真实现lio_sam建图和ndt_matching定位
    国内AI大赛汇总
  • 原文地址:https://blog.csdn.net/duke_ding2/article/details/126103537