如有兴趣了解更多请关注我的个人博客https://07xiaohei.com/
(三)函数调用
1. 基本内容:
定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。
Python中需要显式说明函数的调用,并为其指定参数(如需要参数)完成函数功能。
函数的调用可以是直接调用,可以是通过另一个函数调用来执行,也可以直接用Python提示符执行。
def caixukun():
print("小黑子露出鸡脚了")
def usefun():
print("usefun",end=",")
caixukun()
usefun()
caixukun()
# 运行结果:
# usefun,小黑子露出鸡脚了
# 小黑子露出鸡脚了
2. 内置函数:
Python中提供了众多的内置函数,这些函数不需要我们定义,可以直接调用。
一旦我们定义了与内置函数名相同的函数名,就会覆盖内置函数,但因为一般内置函数会有参数检查,所以不建议直接覆盖定义。
具体的内置函数种类众多,数量巨大,这里不一一讲述,详细函数根据需要查询使用即可。
result = input("请输入内容:")
print(result)
def input():
print("input变为print,输出了1")
input()
# 运行结果:
# 请输入内容:10
# 10
# input变为print,输出了1
3. 参数传递:
在调用函数时,通常会传递参数,函数内部的代码保持不变,针对不同的参数处理不同的数据。
形参和实参:
- 形参是定义函数时的参数变量,Python中没有类型,主要是为了代码块的使用和确定参数数量。
- 实参是调用函数时使用的实际参数变量。
- 参数传递实际就是把实参的引用传递给形参,使用实参的值来执行函数体的过程。
Python中,类型属于对象,变量是没有类型的——变量是一个对象的引用(或者说是指针),因此函数中的参数没有类型,在传递时必须考虑传递的参数类型是否能够符合要求,不会引发错误。
(1)可更改和不可更改对象:
详见之前的博客——python基础——数据类型(一)简介 - ZZHの个人博客 (07xiaohei.com)
了解了这个之后,就可以说明参数传递的底层实现了。
从结果上看,Python的参数传递中,可变类型的参数传递类似于c++的引用传递,被传递的实参对象在函数中修改时会影响到外部的对象;而不可变类型的参数传递类似于c++的值传递,被传递的实参对象在函数中进行任何修改不会影响到外部的对象,因为实际上传递时实参对象是被复制了一份进入了函数,原对象不会受到其影响。
而实际上,Python的参数传递是赋值传递,即对象的引用传递,由于Python的所有数据类型都是对象,所以参数传递实际上是复制出新的变量,让新的变量和原变量指向一个相同的对象。
——换言之,Python里的参数传递,外部的变量和函数的变量指向的是一个具体的对象,不是一个具体的内存地址。因此对象可变时,改变对象,所有指向这个对象的变量都会改变,表现为引用传递;而对象不可变时,赋值等操作实际是创建了一个新的对象并让函数的变量指向新的对象,这样无论如何修改自然不会影响到外部变量指向的对象,此时表现为了值传递。
下面通过几个例子来加深理解:
def addn(nn,k):
print(id(nn))
nn = nn+k
print(id(nn))
print(nn)
n = 3
addn(n,5)
print(id(n))
print(n)
# 运行结果:
# 140709379830632
# 140709379830792
# 8
# 140709379830632
# 3
从上面可以看出,外部变量n和函数传递变量nn指向的对象开始是相同的,都是id为140709379830632的对象,而进行了赋值操作后,nn指向的对象发生了改变,对应了140709379830792,和原来不同,所以其对象值的改变不会影响到外部变量n,因此表现为了值传递。
def addn(n,k):
print(id(n))
print(id(n[0]))
print(n)
print()
n[0] = n[0]+k
print(id(n))
print(id(n[0]))
print(n)
print()
n= [1,2,3,4]
print(id(n))
print(id(n[0]))
print(n)
print()
addn(n,5)
print(id(n))
print(id(n[0]))
print(n)
# 运行结果:
# 2740557338880
# 140709379830568
# [1, 2, 3, 4]
#
# 2740557338880
# 140709379830568
# [1, 2, 3, 4]
#
# 2740557338880
# 140709379830728
# [6, 2, 3, 4]
#
# 2740557338880
# 140709379830728
# [6, 2, 3, 4]
这里就可以明显的看出实际的变化了。
首先,n变量指向了一个列表对象,id为2740557338880,此时n[0]也指向了一个number对象,id为140709379830568,输出为1,2,3,4
然后传入函数,生成函数变量n,这个n和外部变量n的指向全部都是相同的对象,说明了传递的底层逻辑。
再进行赋值操作,n[0]指向的对象发生了改变,id变为了140709379830728,但是注意,n的指向没有发生改变,所以内外的变量n访问n[0]指向对象时都是赋值之后的n[0],表现为了引用传递,内外的输出均变为6,2,3,4——其实质,就是因为列表可变,而number不可变导致的。
综上,如果想要通过函数来改变某个变量的值,可以直接将可变数据类型当作参数传入,直接进行修改,也可以针对不可变数据类型,创建一个新对象,来保存修改后的值,然后将其返回给原变量完成修改。
一般建议使用第二种方式,因为其简明易懂,不易出错。
(2)参数类型:
-
位置参数:必须以在被调用函数中的定义的准确顺序来传递。
当不含默认参数时,传入函数的参数数量必须和声明时的参数数量相一致。
是最普通的传递方式。
def add_three(a,b,c): return a+b+c print(add_three(1,2,3)) # 运行结果: # 6
-
关键字参数(命名参数):和函数调用密切相关,函数调用使用关键字参数来确定传入参数值。
关键字参数允许函数调用的参数顺序和声明的不一致,由解释器用参数名来匹配参数值。
调用的形参列表格式:参数名k1=实参值1,参数名k2=实参值2,…,参数名kn=实参值n
关键字参数必须在位置参数的后面,否则解释器不能识别结果。
了解即可,一般不常用。
def add_three(a,b,c): return a+b*2+c*3 print(add_three(c=1,a=2,b=3)) # 运行结果: # 11 (2+3*2+1*3)
-
默认值参数:为一个参数指定默认值,如果没有给该参数传值,则按默认值传入处理。
具有默认值的参数也叫做缺省参数。
缺省参数应当在参数列表的末尾。也就是默认参数必须在所有位置参数的后面设定,否则解释器不能准确识别对应结果。
调用时,允许参数数量在所有位置参数数量和全部参数数量之间,赋值从左到右逐个进行,剩余则使用默认参数。
注意:默认值只会执行一次,当默认值为可变对象时,要注意多次调用可能会出现每次结果保留了上次调用结果的可能性。
def add_two_three(a,b,c=0): return a+b+c print(add_two_three(1,2)) print(add_two_three(3,4,5)) def app(i=0,list_use = [] ): list_use.append(i) print(list_use) app() app(1) app(2) app(3) #运行结果: # 3 # 12 # [0] # [0, 1] # [0, 1, 2] # [0, 1, 2, 3]
默认参数可以和关键字参数一起使用:
关键字参数必须跟随在位置参数的后面。传递的所有关键字参数必须与函数接受的其中一个参数匹配,它们的顺序并不重要。这也包括非可选参数。不能对同一个参数多次赋值。
-
不定长参数(多值参数):函数处理参数个数不能确定时使用,对于参数个数可能超过声明时个数需要声明多值参数将多出的参数打包处理。
Python的多值参数有两种:
- 接收元组的参数包:参数名前加一个*,一般用*args表示。
- 接收字典的参数包:参数名前加两个**,一般用**kwargs表示。
形参列表的格式为:([formal_args,] *var_args_tuple ): / ([formal_args,] **var_kwargs_dict):
增加的*和**就是解包的表现。
解包参数列表:当参数在列表或者元组中,又需要为单独的位置参数的函数提供参数时,通过对其解包完成参数传递。
对于元组,每个元素作为一个参数传递,按顺序进行;对于字典,参数名作为键,参数传入值作为值,形成键值对完成传递。
强制命名参数:如果有其他参数被定义到了多值参数的后面,称为强制命名参数,调用时必须显式使用关键字参数传递值。
def add_many(*args): print(args) count=0 for i in args: count =count+i print(count) add_many(1) add_many(1,2) add_many(1,2,3) add_many(1,2,3,4,5,6,7,8,9) tuple_temp=(1,2,3,4,5,6,7,8,9) add_many(*tuple_temp) #运行结果: # (1,) # 1 # (1, 2) # 3 # (1, 2, 3) # 6 # (1, 2, 3, 4, 5, 6, 7, 8, 9) # 45 # (1, 2, 3, 4, 5, 6, 7, 8, 9) # 45 def add_many(**kwargs): count=0 for i in kwargs.values(): count = count+int(i) print(count) print(kwargs) temp ={'a':'1','b':'2','c':'3'} add_many(**temp) #运行结果: # 6 # {'a': '1', 'b': '2', 'c': '3'} def add_three(a,b,c): return a+b*2+c*3 list_temp=(1,2,3) print(add_three(*list_temp)) dict_temp={'b':1,'a':2,'c':3} print(add_three(**dict_temp)) # 运行结果: # 14 # 13 def add_more_solid(*args,solid): count=solid for i in args: count =count + i print(count) tuple_temp=(1,2,3,4,5) add_more_solid(*tuple_temp,solid =10) #运行结果: # 25
4.内嵌函数:
在一个函数内创建另一个函数,通常还需要在其中调用,一旦脱离函数,创建的函数也不能够再使用了。
def add_three(a,b,c):
def add_two(x,y):
print(x+y)
return x+y
return add_two(a,b)+c
print(add_three(1,2,3))
# 运行结果:
# 3
# 6