Python中的函数

本文详细探讨了Python中的函数,包括它们的作用、分类、定义和调用方式。重点讲解了函数参数的多种形式,如默认值、可变参数、关键字参数和混合调用,以及参数解构。还介绍了函数的作用域,包括全局和局部作用域,以及`global`和`nonlocal`语句的使用。此外,文章提到了函数的返回值和闭包等高级主题,帮助读者全面掌握Python函数的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数

  • 由诺干语句组成的语句块、函数名、参数列表构成,它是组织代码的最小单元
  • 能完成一定的功能
  • 是一个可调用对象,可以使用callable(函数名)判断该名称是否是一个可调用对象
  • 数学定义:y=f(x), y是x的函数,x是自变量。y=f(x0,x1,…xn)
函数的作用
  • 结构化编程对代码的最基本封装,一般按照功能组织一段代码
  • 封装的目的为了复用,减少冗余代码
  • 代码更加简洁美观、可读易懂
函数的分类
  • 内建函数,如:max()、reversed()等
  • 库函数,如math.ceil()
函数的定义
  • def语句定义函数
    • def 函数名(参数列表):
      函数体(代码块)
      [return 返回值]
    • 函数名就是标识符。命名要求和标识符命名要求一样。
    • 语句块必须缩进,约定4个空格
    • Python的函数如果没有return语句,那么默认会返回None值
    • 定义中的参数列表成为形式参数,只是一种符号表达(标识符),简称形参
    • 例如:
    ##定义无参函数
    def Hellogdy():
        print("gdy")
    ##函数的调用
    Hellogdy()
    ##加法函数
    def add(x,y):
        return x+y
    ##调用函数
    print(add(7,8))
    
函数的调用
  • 函数定义,只是在内存中声明了一个函数。需要调用才能执行该函数
  • 调用方法:函数名加上小括号,如果有必要参数,需要在括号内写上参数
  • 调用时写的参数是实际参数,是实实在在传入的值,简称实参

函数的参数

  • 参数是调用函数时传入的。参数要和函数定义的形参想匹配(可变参数例外)
  • 位置参数:按照参数定义顺序传入实参
    • 例如: def f(x,y,z)调用使用f(1,3,5)
  • 关键字参数:使用形参的名字来传入实参的方式。如果使用了关键字参数,那么传参顺序就可以和定义顺序不同
    • 例如: def f(x,y,z)调用使用f(x=1,z=2,y=3)
  • 混合调用
    • 如果使用位置参数和关键字参数混合调用。那么位置参数必须要在关键字参数之前传入,参数不能重复
      *例子:
def add(x,y,z):
    return x+y+z
#使用位置参数调用
add(1,2,3) #此时形参x=1,y=2,z=3的方式按照顺序获取实参
#使用关键字参数调用
add(z=1,y=3,x=5) #直接指定形参的值调用函数
#形参和实参混合调用
add(4,z=5,y=3) ##正确, #错误写法add(4,x=3,z=8)
函数参数的默认值(缺省值)
  • 在定义函数的形参时,给形参附加一个值。
  • 例如:
#4为函数形参x的默认值,5为函数形参y的默认值
def add(x=4,y=5):
    return x+y
  • 注意:如果定义函数时,有缺省值的参数必须放在没有缺省值参数的后面。
    例如:
def add(x,y=5):
    return x+y
  • 作用
    • 参数的默认值可以在未传入足够的实参时,对没有给定的参数赋值即为默认值
    • 参数非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用
函数的可变参数
  • 可变位置参数

    • 表示方法:【*变量名】
    • 一个形参可以匹配任意个参数。会收集多个实参组成一个tuple
    • 注意:
      1. 可变位置参数必须要在位置参数后面
      2. 可变位置参数不能使用关键字传参

    例如:

    def addsum(*args):
        sum = 0
        for x in args:
            sum += i
        return sum
    #调用
    addsum(1,23,4,5,65,6) #返回所有参数的和
    def addsum2(a,b=4,*args):
        print(a,b,args)
    
  • 可变关键字参数

    • 表示方法:【**变量名】
    • 形参前使用**符号,表示可以接收多个关键字参数
    • 收集的实参名称和值组成一个字典
    • 注意:
      • 可变关键字参数,必须要在所有参数最后面(即,参数,可变参数,可变位置参数)
    • 例如:
    ##可变关键字传参定义
    def showname(**kwargs):
        for k,v in kwargs.items():
            print("{}:{}".format(k,v))
    ##调用
    showname(a=1,b=2,c=3)
    
混合定义
  • 混合使用参数的时候,可变关键字参数要放到参数列表的最后,普通参数需要放到参数列表的前面,关键字参数 要放在可变位置参数可变关键字参数之间

  • 混合定义参数顺序:普通参数,缺省值参数,可变位置参数,关键字参数(keyword-only)(可带缺省值),可变关键字参数

  • 混合定义参数传参顺序:位置参数关键字参数前面

    • 注意:混合传参时,可变关键字参数名称不能与普通参数名称相同。
    #定义:函数如下: 
    def showname(name,sex=6,*args,b=15,c,**kwargs):
        print("name={}\nsex={}\nargs={}\nb={}\nc={}\nkwargs={}".format(name,sex,args,b,c,kwargs))
    ##调用
    showname(1,5,4,5,b=23,c=34,hh=23)
    
    • 其中name,set为普通参数,其中sex带默认值6,name在接受值时优先级最高
    • args为可变参数,会收集函数中除了name和set外(即从第三个普通参数开始)其余剩下的普通参数(不包括关键字参数)
    • b为带默认值的关键字参数,c为必须传入的关键字参数
    • kwargs为可变关键字参数,会收集除key不在{a,b}内的所有关键字参数。
    • 例如:
      fun001
参数定义中的特殊符号:
  • 例如:def app(*,a,b) 其中 * 号没有特殊意义,但这种方式标识了a,b必须是关键字参数
    • 【*】星号之后的普通形参都变成了keyword-Only(关键字参数)
    def add(*,x=4,y):
        print(x+y)
    add(x=3,y=9)
    
参数解构
  • 给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的解构解开,提取所有元素作为函数的实参

  • 非字典类型使用*解构成位置参数

  • 字典类型使用**解构成关键字参数

  • 提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配

    • 例如:add(**{“x”:4,“y”:5}) #等价于add(x=4,y=5),其中两个星号表示两层解构
    def add(x,y):
        return x+y
    add(*[4,5]) #相当于add(4,5)
    add(*{'a':4,'b':5}) #相当于add("a","b")
    add(**{"x":4,"y":5}) #等价于add(x=4,y=5),其中两个星号表示两层解构
    ##add(**{'a':4,'b':5})# 等价于add(a=4,b=5),由于没有找到对应的参数x,y所以会出错
    
  • 总结

    • 有位置可变参数和关键字可变参数
    • 位置可变参数在形参前使用一个星号*,关键字可变参数使用两个星号**
    • 位置可变参数和关键字可变参数都可以手机若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集成一个dict
    • 混合使用参数的时候,可变关键字参数要放到参数列表的最后,普通参数需要放到参数列表的前面,关键字参数 要放在可变位置参数可变关键字参数之间
    • 混合定义参数传参顺序:位置参数关键字参数前面

函数的返回值

  • Python函数使用return语句返回“返回值”
  • 所有函数都有返回值,如果没有return语句,隐试调用return None
  • return语句并不一定是函数语句块中最后一条语句
  • 一个函数可以存在多个return语句,单是只有一条可以被执行。如果没有一条return语句被执行到,就会隐试调用return None
  • 如果函数执行了return语句,函数就会返回,当被执行的return语句之后的其他语句就不会被执行了
  • return的作用:结束函数调用、返回“返回值”
  • return 不能同时返回多个值。
def showvalue():
    return 1,2,3  #这里实际返回的是将1,2,3封装成一个元组tuple

函数的作用域

  • 每一个函数都会开辟一个作用域
  • 作用域:一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
  • 全局作用域
    • 在整个程序的运行环境中都可见
    • 全局作用域中的变量称为全局变量
    • 使用关键字global 标明的变量,表示该变量为全局变量。(注意:声明全局变量后,如果直接对全局变量做运算,必须要保证外部全局变量中该变量已经赋值了)
    • 全局变量简单示例:
    x = 4 #x为全局变量
    y = 5
    def fun():
        #y += 5 #注意:如果局部变量中要想改变全局变量,必须先使用global先标识改变量为全局变量。
        y = 9  #此时y为fun的局部变量(赋值即定义)
        print(x) #x为全局变量,函数fun可以使用全局变量x
        return None
    fun()
    
  • 局部作用域(或本地作用域)
    • 在函数、类等内部可见
    • 局部作用域中的变量称为局部作用域,其使用范围不能超过其所在局部作用域
    • 如果函数中出现了对某一个变量赋值,那么该变量就会变为本函数的局部变量。(即:赋值即定义)
    • 使用nonloacl标识的变量为外层函数的局部变量,但不能在全局作用域中定义。
    • 例如:
    x = 5  #x为全局变量
    def fun2():
        y = 9  #y为局部变量,作用域为fun2
        print(y)
    print(x)
    
  • 函数的默认值作用域
    • defaults 记录了函数**普通参数(位置参数)**中的默认值。是个元组tuple,不会因为在函数体内改变了局部变量(形参)的值而发生改变
    • kvdefaults 记录了函数**keyword_only(关键字参数)**中的默认值,是一个字典
    # def fun(gdy=[]): #由于位置参数默认值为引用类型,所以gdy实际表示为[]在内存中的地址。所以gdy每次默认获取的也是[]在内存中的存放地址
    #     gdy.append(1)
    #     return gdy
    # print(fun(),fun.__defaults__,id(fun.__defaults__),sep="\t\t")
    # print(fun(),fun.__defaults__,id(fun.__defaults__),sep="\t\t")
    # print(fun(),fun.__defaults__,id(fun.__defaults__),sep="\t\t")
    
    def fun(a=1,b=[],*,c=3,d=[]): #由于关键字参数默认值为引用类型,所以d关键存储的实际是[]在内存中所在的地址
        a = 0
        b.append(1)
        c = 0
        d.append(1)
        return a,b,c,d
    print(fun(),fun.__defaults__,id(fun.__defaults__),fun.__kwdefaults__,id(fun.__kwdefaults__),sep="\t")
    print(fun(),fun.__defaults__,id(fun.__defaults__),fun.__kwdefaults__,id(fun.__kwdefaults__),sep="\t")
    print(fun(),fun.__defaults__,id(fun.__defaults__),fun.__kwdefaults__,id(fun.__kwdefaults__),sep="\t")
    
    fun002
    • 注意如下特殊示例
    def x(a=[],b="ab",c={3,5},d=(1,)):
        a += [5]  # 注意 a += [5] 等价于 a.extend([5])
        b += "cd"
        d += (2,) # 注意:元组tuple和字符串都是不可变类型
        #c += {4,6} #报错,集合不能这样用
    print(x.__defaults__)
    x()
    x()
    print(x.__defaults__)
    
    fun003
函数嵌套:
  • 在一个函数中定义了另外一个函数,内函数能访问外函数的变量,而外部函数不能访问内部函数的变量。
    • 简单示例:
    x = 5 #x为全局变量
    def fun1():
        y = 4 #y为fun1的局部变量
        def fun2(): ##fun2为fun1的内部函数
            k = 6 #k为fun2的局部变量
            return y  #将外部函数变量y作为返回值。(内部函数可以访问外部函数中的变量)
        return fun2   #这里将内部函数对象在内存中的地址作为fun1的返回值。
    m = fun1() #调用fun1可以获取fun1的内部函数fun2
    m() #执行fun1的内部函数
    
global语句
  • global标明变量为全局作用域的变量。
    • global的使用原则:
      1. 外部作用域变量会在内部作用域可见,最好不要再内部作用域中直接改变其值,因为函数的目的就是为了封装,尽量与外界隔离
      2. 如果函数需要使用外部变量,应尽量使用函数的形参定义,并在调用传实参解决
    • 简单示例:
    x = 5  #赋值即定义全局变量  #由于在最外面,此时定义的变量都是全局变量
    y = 4 #赋值即定义全局变量
    def fun():
        global x  #表示在fun作用域内x为全局变量
        def fun2():
            global y #表示在fun2作用域内y也是全局变量
            y +=1  ###注意:如果没有上门global语句定义y的作用域为全局变量,那么直接使用y+=1,由于是赋值语句,系统会认为y为fun2的局部变量。而y+=1此时等价于y = y+1 则在fun2的局部变量中y还没有赋值,即无法进行加减。所以没有上门global y会报错。
            x = 9 #次数的x为fun2的局部变量
            return None
        def fun3():
            x = 1   #赋值即定义,此时x为fun3的局部变量(本地变量)
            print(y) #由于fun3内没有y的定义(赋值即定义),编译器会向上查找,直到找到最近作用域的变量y打印
            print(x) #次数x为fun3作用域的变量x
            return None
        fun2()
        fun3()
        return None
    fun()
    
闭包
  • 内层函数用到了外层函数的局部变量(本地变量,或自由变量).称此现象为闭包
    • 即:自由变量:未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量
  • 当内层函数没有消亡,即闭包对应的变量也不会消亡
  • 可以使用nonlocal标识的变量,为外层函数中的局部变量。但不能在全局作用域中定义。
  • 简单示例:
    函数counter中的内建函数inc引用了外层函数中的局部变量c所有构成了闭包。
def counter():
    c = [0]  #注意此时c为引用地址,引用地址指向[0]
    def inc():
        c[0] += 1 # 注意,此时c引用地址,引用地址指向[0]位置,并操作了[0]
        return c[0]
    return inc #返回内建函数inc
m = counter() #调用counter,获取内建函数,此时m指向inc函数所在的对象
print(m(),m())
c = 100  #此c为全局变量
print(m()) 
nonlocal语句
  • nonlocal:将变量标记为在外层函数中定义的局部变量。但不能在全局作用域中定义
  • 使用nonlocal可以简单的构成闭包
  • 例如:
b = 10  #全局作用域中定义的变量为全局变量
def counter():
    #nonlocal b #无法使用,因为b为全局作用域中的全局变量
    #nonlocal bb #也无法使用,应为counter作用域外就是全局作用域。而全局作用域中定义的变量都是全局变量
    c = 0
    def inc():
        nonlocal c
        c +=1
        return c
    return inc
m = counter() #调用counter,获取内建函数,此时m指向inc函数所在的对象
print(m(),m())
c = 100  #此c为全局变量
print(m()) 
变量名解析原则LEGB
  • Local,本地作用域、局部作用域的local命名空间。函数调用时创建,调用结束消亡
  • Enclosing,Python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间
  • Global,全局作用域,即一个模块的命名空间。模块被import时创建,解析器退出时消亡
  • Build-in,内置模块的命名空间,生命周期从python解释器启动时创建到解析器退出时消亡。例如print(open),print和open都是内置的变量
    所以,一个名词(即变量名)查找顺序就是LEGB
    fun004
函数的销毁
  1. 定义一个函数就是生产一个函数对象,函数名指向的就是函数对象
  2. 可以使用del语句删除函数,使其引用计数减1
  3. 可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减1
  4. Python程序结束时,所有对象都会销毁
  5. 函数也是对象。是否销毁,还是看引用计数是否减为0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值