函数(Function)作为程序语言中不可或缺的一部分,但函数作为一等对象(First-Class Object)却是 Python 函数的一大特性。在python中,所有函数都是一等对象
那到底什么一等对象(First-Class Object)呢?
编程语言理论家把一等对象定义为满足下述条件的程序实体:
- 在运行时创建
- 能赋值给变量或者数据结构中的元素
- 能作为参数传递给函数
- 能作为函数的返回结果
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
具体体现在:
// 函数作为对象,被调用时是不带括号的。1. 函数可以被引用def add(x,y):return x+yfunc=addfunc(1,2) # 结果为32. 函数可以作为容器类型的元素dict={"add":add,"max":max} #结果:{'add': <function add at 0x100661e18>, 'max': <built-in function max>}dic['add'](1,2) # 结果为33. 函数可以作为参数传入另一个函数def foo(x,y,func):return func(x,y)foo(1,2,add) #结果为34. 函数的返回值可以是一个函数,def bar():return addfunc=bar()func(1,2) #结果为3
1. 参数
1.1 形参与实参
在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是:
形式参数:在定义函数时,函数名后面括号中的参数就是形式参数
#定义函数时,这里的函数参数 obj 就是形式参数def demo(obj):print(obj)
实际参数:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数
a = "C语言中文网"#调用已经定义好的 demo 函数,此时传入的函数参数 a 就是实际参数demo(a)
实参和形参的区别,就如同剧本选主角,剧本中的角色相当于形参,而演角色的演员就相当于实参。
1.2 函数传递机制
根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用传递:
- 对形参的修改能够影响到实参就是引用传递。
- 对形参的修改不影响实参则是值传递。
适用类型:
- 值传递(传值):适用于实参类型为不可变类型(字符串、数字、元组);
- 引用传递(传地址):适用于实参类型为可变类型(列表,字典,集合);
原理:
- 值传递:将实际参数值的一个副本传入函数中,在函数中对其操作不会影响原参数。
- 引用传递:将参数的地址传入,在函数内部进行修改,函数外的参数也会随之变化。
def demo(obj) :obj += objprint("形参值为:",obj)print("-------值传递-----")a = "C语言中文网"print("a的值为:",a)demo(a)print("实参值为:",a)print("-----引用传递-----")a = [1,2,3]print("a的值为:",a)demo(a)print("实参值为:",a)
注意:假如要在函数内修改全局变量的值,当值为不可变类型时,需要用到-------值传递-----a的值为: C语言中文网形参值为: C语言中文网C语言中文网实参值为: C语言中文网-----引用传递-----a的值为: [1, 2, 3]形参值为: [1, 2, 3, 1, 2, 3]实参值为: [1, 2, 3, 1, 2, 3]
**global**关键字,x=1def foo():global xx=2foo()print(x) # 结果为2
1.3 参数分类
定义函数时参数的顺序:def func(位置参数,可变位置参数,默认参数,可变关键字参数):pass
1.3.1 位置参数(必备参数)
位置参数是指调用函数时,参数的个数,数据类型,以及输入顺序必须正确,否则会出现语法错误。def register(name,age,sex):print("Name: %s , Age:%d , Sex: %s" %(name,age,sex))if __name__ =="__main__":register("杰克",18,"男")
1.3.2 关键字参数
在调用函数时,实参以key=value的形式传入函数进行赋值,称为关键字参数。
实参也可以按照位置和关键字参数混合使用,但是必须保证关键字参数在位置参数之后,且不可以对一个形参重复赋值。register("lili",sex="male",age=18) # 正确使用register(name="lili",18,sex='male') # 语法异常register(18,name="lili",sex="male") # 语法错误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}
无论是包裹位置参数还是关键字参数,都是一个组包过程。<br />**补充:拆包**<a name="NhKfA"></a># 2. 嵌套函数说到嵌套函数(函数内部定义了函数),就不得不介绍`**global、nonlocal**`关键字。`**global、nonlocal**`关键字只能接变量,**不能接表达式**。- 内部函数调用**外部函数的变量**,使用nonlocal关键字。- 内部函数调用**全局变量**,使用global关键字。全局变量是指在函数外面的变量,可能存在于类中,也有可能在类的外部。外部函数的变量依然是局部变量。```pythonx=1 # x是全局变量def foo():global x #声明调用全局变量,不能写为global x = 2,会报错x=2foo()print(x) #结果为2,假如不声明global,结果则为1def f1():x=2def f2():nonlocal xx=3f2() # 调用f2(),修改f1作用域中名字x的值print(x)f1() # 结果为3
3. 闭包
形成条件:
- 在函数里面再嵌套函数
- 内部函数调用了外部函数的变量(包括外部函数的形参),但是需要修改外部变量的值的话,需要使用
**nonlocal**关键字 - 外部函数返回了内部函数的函数名
装饰器是闭包的一种应用。装饰器的介绍见下篇文章。def out(num1):def inner(num2):result = num1+num2print(result)return innerout(1)(2) # num1=1,num2=2
