函数(Function)作为程序语言中不可或缺的一部分,但函数作为一等对象(First-Class Object)却是 Python 函数的一大特性。在python中,所有函数都是一等对象

那到底什么一等对象(First-Class Object)呢?
编程语言理论家把一等对象定义为满足下述条件的程序实体:

  • 在运行时创建
  • 能赋值给变量或者数据结构中的元素
  • 能作为参数传递给函数
  • 能作为函数的返回结果

在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
具体体现在:

  1. // 函数作为对象,被调用时是不带括号的。
  2. 1. 函数可以被引用
  3. def add(x,y):
  4. return x+y
  5. func=add
  6. func(1,2) # 结果为3
  7. 2. 函数可以作为容器类型的元素
  8. dict={"add":add,"max":max} #结果:{'add': <function add at 0x100661e18>, 'max': <built-in function max>}
  9. dic['add'](1,2) # 结果为3
  10. 3. 函数可以作为参数传入另一个函数
  11. def foo(x,y,func):
  12. return func(x,y)
  13. foo(1,2,add) #结果为3
  14. 4. 函数的返回值可以是一个函数,
  15. def bar():
  16. return add
  17. func=bar()
  18. func(1,2) #结果为3

1. 参数

1.1 形参与实参

在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是:

  • 形式参数:在定义函数时,函数名后面括号中的参数就是形式参数

    1. #定义函数时,这里的函数参数 obj 就是形式参数
    2. def demo(obj):
    3. print(obj)
  • 实际参数:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数

    1. a = "C语言中文网"
    2. #调用已经定义好的 demo 函数,此时传入的函数参数 a 就是实际参数
    3. demo(a)

    实参和形参的区别,就如同剧本选主角,剧本中的角色相当于形参,而演角色的演员就相当于实参。

1.2 函数传递机制

根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用传递:

  • 对形参的修改能够影响到实参就是引用传递。
  • 对形参的修改不影响实参则是值传递。

适用类型:

  1. 值传递(传值):适用于实参类型为不可变类型(字符串、数字、元组);
  2. 引用传递(传地址):适用于实参类型为可变类型(列表,字典,集合);

原理:

  • 值传递:将实际参数值的一个副本传入函数中,在函数中对其操作不会影响原参数。
  • 引用传递:将参数的地址传入,在函数内部进行修改,函数外的参数也会随之变化。
    1. def demo(obj) :
    2. obj += obj
    3. print("形参值为:",obj)
    4. print("-------值传递-----")
    5. a = "C语言中文网"
    6. print("a的值为:",a)
    7. demo(a)
    8. print("实参值为:",a)
    9. print("-----引用传递-----")
    10. a = [1,2,3]
    11. print("a的值为:",a)
    12. demo(a)
    13. print("实参值为:",a)
    1. -------值传递-----
    2. a的值为: C语言中文网
    3. 形参值为: C语言中文网C语言中文网
    4. 实参值为: C语言中文网
    5. -----引用传递-----
    6. a的值为: [1, 2, 3]
    7. 形参值为: [1, 2, 3, 1, 2, 3]
    8. 实参值为: [1, 2, 3, 1, 2, 3]
    注意:假如要在函数内修改全局变量的值,当值为不可变类型时,需要用到**global**关键字,
    1. x=1
    2. def foo():
    3. global x
    4. x=2
    5. foo()
    6. print(x) # 结果为2

    1.3 参数分类

    定义函数时参数的顺序:
    1. def func(位置参数,可变位置参数,默认参数,可变关键字参数):
    2. pass

    1.3.1 位置参数(必备参数)

    位置参数是指调用函数时,参数的个数,数据类型,以及输入顺序必须正确,否则会出现语法错误。
    1. def register(name,age,sex):
    2. print("Name: %s , Age:%d , Sex: %s" %(name,age,sex))
    3. if __name__ =="__main__":
    4. register("杰克",18,"男")

    1.3.2 关键字参数

    在调用函数时,实参以key=value的形式传入函数进行赋值,称为关键字参数。
    实参也可以按照位置和关键字参数混合使用,但是必须保证关键字参数在位置参数之后,且不可以对一个形参重复赋值。
    1. register("lili",sex="male",age=18) # 正确使用
    2. register(name="lili",18,sex='male') # 语法异常
    3. register(18,name="lili",sex="male") # 语法错误
    4. register("lili",sex='male',age=18,name='jack') #TypeError

    1.3.3 默认参数

    默认参数是指允许函数参数有默认值,如果调用函数时不给参数传值,参数将获得默认值。Python通过在函数定义的形参名后加上赋值运算符(=)和默认值给形参指定默认参数值。
    需要注意的是:
  • 默认参数值是一个不可变参数。
  • 默认参数的值在函数定义时被赋值一次。

    1.3.4 不定长参数

    不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数的场景。可以用于包裹位置参数,关键字参数,来进行参数传递。 ```java 包裹位置参数传递 ——————>调用时,传进去的所有参数都会被args变量收集,根据传进参数的位置合并为一个元组 def user_info(*args): print(args) user_info(“Tom”,) # 输出:(‘Tom’,) user_info(“Tom”,18) # 输出:(‘Tom’, 18)

包裹关键字参数传递 ——————>调用时,传进去的所有参数都会被kwargs变量收集,根据传进参数的位置合并为一个字典 def user_info(**kwargs): print(kwargs) user_info(name=”Tom”,age=18,id=1101) #输出:{‘name’: ‘Tom’, ‘age’: 18, ‘id’: 1101}

  1. 无论是包裹位置参数还是关键字参数,都是一个组包过程。<br />**补充:拆包**
  2. <a name="NhKfA"></a>
  3. # 2. 嵌套函数
  4. 说到嵌套函数(函数内部定义了函数),就不得不介绍`**global、nonlocal**`关键字。`**global、nonlocal**`关键字只能接变量,**不能接表达式**。
  5. - 内部函数调用**外部函数的变量**,使用nonlocal关键字。
  6. - 内部函数调用**全局变量**,使用global关键字。
  7. 全局变量是指在函数外面的变量,可能存在于类中,也有可能在类的外部。外部函数的变量依然是局部变量。
  8. ```python
  9. x=1 # x是全局变量
  10. def foo():
  11. global x #声明调用全局变量,不能写为global x = 2,会报错
  12. x=2
  13. foo()
  14. print(x) #结果为2,假如不声明global,结果则为1
  15. def f1():
  16. x=2
  17. def f2():
  18. nonlocal x
  19. x=3
  20. f2() # 调用f2(),修改f1作用域中名字x的值
  21. print(x)
  22. f1() # 结果为3

3. 闭包

形成条件:

  • 在函数里面再嵌套函数
  • 内部函数调用了外部函数的变量(包括外部函数的形参),但是需要修改外部变量的值的话,需要使用**nonlocal**关键字
  • 外部函数返回了内部函数的函数名
    1. def out(num1):
    2. def inner(num2):
    3. result = num1+num2
    4. print(result)
    5. return inner
    6. out(1)(2) # num1=1,num2=2
    装饰器是闭包的一种应用。装饰器的介绍见下篇文章。