函数(Function)作为程序语言中不可或缺的一部分,但函数作为一等对象(First-Class Object)却是 Python 函数的一大特性。在python中,所有函数都是一等对象
那到底什么一等对象(First-Class Object)呢?
编程语言理论家把一等对象定义为满足下述条件的程序实体:
- 在运行时创建
- 能赋值给变量或者数据结构中的元素
- 能作为参数传递给函数
- 能作为函数的返回结果
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
具体体现在:
// 函数作为对象,被调用时是不带括号的。
1. 函数可以被引用
def add(x,y):
return x+y
func=add
func(1,2) # 结果为3
2. 函数可以作为容器类型的元素
dict={"add":add,"max":max} #结果:{'add': <function add at 0x100661e18>, 'max': <built-in function max>}
dic['add'](1,2) # 结果为3
3. 函数可以作为参数传入另一个函数
def foo(x,y,func):
return func(x,y)
foo(1,2,add) #结果为3
4. 函数的返回值可以是一个函数,
def bar():
return add
func=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 += obj
print("形参值为:",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=1
def foo():
global x
x=2
foo()
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关键字。
全局变量是指在函数外面的变量,可能存在于类中,也有可能在类的外部。外部函数的变量依然是局部变量。
```python
x=1 # x是全局变量
def foo():
global x #声明调用全局变量,不能写为global x = 2,会报错
x=2
foo()
print(x) #结果为2,假如不声明global,结果则为1
def f1():
x=2
def f2():
nonlocal x
x=3
f2() # 调用f2(),修改f1作用域中名字x的值
print(x)
f1() # 结果为3
3. 闭包
形成条件:
- 在函数里面再嵌套函数
- 内部函数调用了外部函数的变量(包括外部函数的形参),但是需要修改外部变量的值的话,需要使用
**nonlocal**
关键字 - 外部函数返回了内部函数的函数名
装饰器是闭包的一种应用。装饰器的介绍见下篇文章。def out(num1):
def inner(num2):
result = num1+num2
print(result)
return inner
out(1)(2) # num1=1,num2=2