1 为什么使用函数
在没有接触函数时,有时候需要将一个功能多次写,如果需要修改其中一个变量,则需要把所有实现该功能的代码一处一处改。不利于代码维护,代码量大了,组织结构也会很不清晰。
所以总结不使用函数的确定:
- 代码冗余(重复代码多)
- 不利于维护(修改麻烦)
- 可读性差(组织结构不清晰)
函数的优点:
- 解决代码重用问题
- 统一维护
- 程序的组织结构清晰,可读性强
# 2 函数的定义
函数种类:
- 内置函数:内置在python解释器的函数
- 自定义函数:
def funtion_name(args):
"""描述信息""" #可通过function_name.__doc__查看
函数主体
return
自定义函数的种类
-
空函数
def abc(): """描述信息""" pass
空函数使用在布置程序大致框架时,不用具体实现函数的功能。
-
有参函数
def abc(args): """描述信息""" 函数主体 return xxx
通常有参的函数,都是有返回值的。
-
无参函数
def abc(): """描述信息""" 函数主体
通常有参的函数,都是有返回值的。
# 3 函数调用 语句形式:函数名() 表达式形式:res=10*abc(1) 函数调用作为另一个函数的参数:print(abc()) 注意:我们调用一个不存在的函数 ```python func() #NameError: name 'func' is not defined #a=b #NameError: name 'b' is not defined ``` 这里返回的是,“NameError”,函数名未定义。所以,在python中,我们可以理解为**定义函数,就是定义变量名**。
# 4 函数的返回值 python中函数的返回值可以是任意类型。 1. 没有return或return为空,返回的是None。 2. 可以返回多个函数值(本质是打包成一个元组) 3. 如果有多个return(语法上不会报错),但返回第一个遇到的return。
注意:
def my_min(x:int,y:int) ->int:
pass
print(my_min.__annotations__) #{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
该方法适合那些从c++,java转到Python的。
# 5 函数的参数 定义函数时的参数,就是形式参数。 调用函数时的参数,就是实际参数。 函数调用时,会把实参绑定到形参,结束调用时,解除绑定。
#形参、实参都是不可变
def foo1(a):
a=3
print(a)
x=1
foo1(x) #3
print(x) #1
foo1里面重新给a赋值不会影响x,为什么?我们可以这样理解,实参是本身存的一个变量的值,而形参存的是一个变量名(没有值)。当发起函数调用时,形参会绑定实参的值,如果在函数内给形参赋值,形参会绑定新的值。(可以和变量与变量的赋值类比)所以不会改变实参的值。
#形参是可变类型
def foo2(a,L=[]):
L.append(a)
print(L)
foo2(1) #[1]
foo2(2) #[1, 2]
foo2(3) #[1, 2, 3]
当foo2加载到内存,L指向的是[],当每一次调用函数时,都是在向同一个[]附加数据。
#改良版
def foo2(a,L=None):
if L is None:
L=[]
L.append(a)
print(L)
foo2(1) #[1]
foo2(2) #[2]
foo2(3) #[3]
#实参是可变类型
def foo3 (L):
L.append("abc")
print("foo",L)
li=[1,2,3]
foo3(li)
print(li) #[1, 2, 3, 'abc']
当把一个列表li当实参传到函数时,是将列表li的地址传给形参,所以形参所做的修改,都会影响实参。建议:不要使用可变对象作为实参。
a=1
def foo3(x=a):
print(x)
a=3
foo3() #1
这里要区别于java等编译型语言。python作为一门解释型语言,是一边解释一边执行的。当它执行到foo3(),形参x=1被写入内存的。当执行到a=3时,并不会影响x的值。
6 函数的实参
def func(a,b,c)
实参传值的方式:
-
按位置传参:func(1,2,3)
-
按关键字传值:func(a=1,b=2,c=3)
-
混合传值:func(a=1,b=2,c=3)
混合传值注意事项:
- 按位置传值必须在按关键字传值之前
- 一个形参只能接收一个传值,如:func(1,2,b=2)会报错,此时b接收了2个值
7 函数的形参
- 位置参数:必须传值
- 默认参数:可传可不传,且必须放位置参数后面
func(a,b=2) - 可变参数 *args 可以理解为一堆位置参数
先按位置传值,剩下的被*接收,打包成一个元组,再传给args#从形参的角度,形参带* def func(a,b,*args) func(1,2,3,4,5,6) result: a=1 b=2 args=(3,4,5,6)
#从实参的角度,实参带* def foo1(x,y,z): print(x) print(y) print(z) foo1(*(1,2,3)) #如果去掉*,会把整个元组赋值给x,此时y和z没有值会报错
当实参遇到*(1,2,3),需要把它拆成1,2,3这样来看
-
关键字传值 **kwargs 按关键字传值多余的被**接收,打包成一个字典,传给kwargs
#从形参的角度,形参带** def foo2(**kwargs): print(kwargs) foo2(a="aa",b="bb") #从实参的角度,实参带** def foo3(x,y,z=1): print(x) print(y) print(z) foo3(**{"x":"a","y":"b"})
-
命名关键字 *,args 用于指定必须以某些关键字来传值
def foo4(*,x,y=2):
print(x)
print(y)
foo4(x=1)
命名关键字参数不能和可变参数混用
形参的顺序:
def func1(a,b=2,*args,**kwargs)
def func2(a,b=2,*,c=3,**kwargs)
8 nonlocal
nonlocal关键字,只在python3才有。
nonlocal 的作用就是将变量添加到上一层函数的局部命名空间中。
def layer_1():
count=1
def layer_2():
count=2
def layer_3():
nonlocal count
count=3
print(count)
print(locals())
layer_3()
print(count)
print(locals())
layer_2()
print(count)
print(locals())
layer_1()
结果:
3
{'count': 3}
3
{'layer_3': <function layer_1.<locals>.layer_2.<locals>.layer_3 at 0x000002B39BF7BB70>, 'count': 3}
1
{'layer_2': <function layer_1.<locals>.layer_2 at 0x000002B39BF7BAE8>, 'count': 1}
在第3层定义的nonlocal count,count=3影响到第2层count的结果,但第1层没有任何影响