引入
如果在你的程序中有一段代码需要频繁使用,怎么办?最简单粗暴的办法就是,在程序中重复的编写这段代码。
如果这段代码的某些功能需要更改,怎么办?最简单粗暴的办法就是,找到所有用到这段代码的地方逐一进行修改。
您觉得上述的解决办法如何?--- 您要是敢这么写,分分钟被开除~
解决代码重复
上述解决问题的方式最大的问题就是代码重复,用到一次就重复一次,无疑在现实生活中也可以找到例子,比如每次用Python写程序的时候都重复装一遍解释器和pycharm吗?显然不是,只安装一次,写代码的时候直接拿来用就OK了。这里的解释器或者pycharm就是上述例子中被频繁使用的代码,可以事先准备好,用的时候直接拿来用。
在程序中具备某种功能的工具指的就是一个函数,事先准备好就是函数的定义,使用就是函数的调用。
函数定义
函数的使用必须遵循先定义再使用的原则。
函数的定义相当于把一段代码保存,然后将这段代码的内存地址赋值给函数名,函数名就是对这段代码的引用,函数的定义和变量的定义是类似的。没有定义函数直接使用就相当于在使用一个不存在的变量。
定义函数基本语法如下:
def 函数名(参数1, 参数2....):
'''
函数描述
'''
函数体代码
return 值
'''
def - 定义函数的关键字
函数名 - 指向函数体代码内存地址,对函数体代码进行引用,函数名应该见名知意
括号 - 括号内定义参数,参数可有可无
冒号 - 括号后要加冒号,下一行缩进后书写函数体代码
函数描述 - 描述函数的具体功能,不是必要的,但是建议加上
函数体 - 实现功能的具体代码
return 值 - 函数的返回值,非必要
'''
函数的定义有三种方式,如果把函数比喻为一个工厂,函数的参数就是送进工厂的原材料,但是有些工厂是不需要外部提供原材料。
第一种:无参函数
def func():
print('我不需要外界为我提供参数')
第二种:有参函数
def func(a, b):
print('需要外界传参给我')
print(a, b)
第三种,空函数
# 函数体代码为pass为空函数,可以用来在构思项目的时候用空函数代替,当代码构思完成可以将pass替换掉
def func():
pass
函数调用与返回值
函数的使用分为定义和调用阶段,在定义函数时只检测python语法,不执行函数体代码,函数名加括号就是调用函数,只有调用函数才会执行函数体代码。
# 定义函数阶段
def func():
print('func')
func1()
def func1():
print('func1')
# 调用函数阶段
func()
# 执行结果
func
func1
在定义函数阶段,没有语法错误,在调用阶段,两个函数的代码都在内存中,所以也没有任何错误。
如果想要获取函数体代码的执行结果,就需要用到return关键字。
return是一个函数结束的标志,一个函数中可以包含多个return,但是只要执行到其中某一个return函数就结束了,并且会把return后的值作为本次调用的结果返回。
return返回值有三种情况:
第一种,返回None
# 省略return
def func():
print('没有return')
res = func() # 使用变量接收函数执行后的返回值
print(res) # None
# return后不跟返回值
def foo():
print('return后不跟返回值')
return
res = foo()
pritn(res) # None
# return返回None
def func():
return None
第二种,返回一个值,可以是任意数据类型的值
def func():
return [1, 2]
第三种,返回多个值,用逗号分隔开,会被return返回成元组
def func():
return 1, 2 # 相当于return (1, 2)
函数的调用也可以分为三种不同的形式。
第一种,函数名加括号直接调用
def func():
print('func')
func()
第二种,表达式形式
def add(a, b):
return a+b
res = add(1, 2) # 函数返回值赋值给变量
res1 = 10 * add(1, 2) # 对函数返回值进行计算等其他操作
第三种,函数返回值作为参数
def add(a, b):
return a+b
res = add(1, add(2, 3)) # 将函数返回值作为函数的参数
函数的参数
形参和实参
函数的参数分为形式参数和实际参数,简称形参和实参。
形参,在定义函数时,括号内声明的参数,本质就是一个变量名,用来接收函数外部传来的值。
实参,在调用函数时,括号内传入的值,值可以是任意类型的数据。
在调用有参函数时,实参(值)会赋值给形参(变量名)。
def add(a, b): # a b是形参,相当于变量名
print(a + b)
add(1, 2) # 1 ,2是实参,相当于变量值,分别赋值给a b
形参和实参具体使用
位置参数
指的是按照顺序定义的参数,需要从定义函数和调用函数两个角度去看。
在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值。
def func(x, y):
print(x, y)
func() # TypeError: func() missing 2 required positional arguments: 'a' and 'b'
在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应。
def func(x, y):
print(x, y)
func() # TypeError: func() missing 2 required positional arguments: 'a' and 'b'
关键字参数
在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值。
def func(x, y):
print(x, y)
func(y=10, x=12)
需要注意的是,实参可以混合使用位置和关键字传参,但是必须保证关键字参数在位置参数后面,并且不能对同一个形参重复赋值。
def func(x, y):
print(x, y)
func(10, y=10) # 正确
func(x=10, 20) # 错误
func(10, 20, y=30) # 错误,对y重复赋值了
默认参数
默认参数,指的是在定义函数的时候就已经为形参赋值。默认参数必须在位置参数之后。
当函数有多个形参时,可以将值改变较少的参数定义成默认参数,默认参数可以在调用时修改值。
def func(x, y=1):
print(x, y)
func(1) # 正确
func(1, 2) # 正确
可变长度参数以及*
/**
的使用
在定义函数时如果还不确定需要多少个参数,就可以使用可变长度参数占位。
参数的长度可变就是指在调用函数的时候,实参的个数可以不固定,由于实参的格式有关键字实参和位置实参,因此形参提供了两种可变长度参数---可变长度位置参数,可变长度关键字参数。
在定义函数时,如果在最后一个形参名前加*
,在调用函数时,多出来的位置实参,都会被*
接收,以元组的形式赋值给*
后的形参,约定俗成,接收多余位置参数的形参名为args。
def func(x, y, z=1, *args):
print(x, y, z)
print(args)
func(1, 2, 3, 4, 5, 6) # 实参1,2,3按照位置为形参x y z赋值,多余的被*接收作为元组赋值给args
在调用上述func函数的时候,事先有一个可迭代对象(字符串、列表、字典、元组),可以通过*
将这些对象中的每个元素作为实参传递给args.
def func(x, y, z=1, *args):
print(x, y, z)
print(args)
func(1, 2, 3, *'abc')
# * 的作用相当于for循环,将可迭代对象中的每个元组取出作为实参,如果没有*的话,'abc'就是一个普通的位置参数
如果在定义函数时,在最后一个形参名前加**
,在调用函数时,多出来的关键字参数都会被**
接收,以字典的形式赋值给**
后面的参数,约定俗成,接收多余关键字参数的形参名是kwargs。
def foo(x, y, z, **kwargs):
print(x, y, z)
print(kwargs)
foo(1, 2, 3, a=1, b=2) # 实参1,2,3按照位置为形参x y z赋值,多余的关键字实参被**接收作为字典赋值给kwargs
在调用上述foo函数时,可以将字典作为参数,通过**
将字典中的每个键值对作为关键字参数传递给函数
foo(**{'x': 1, 'y':2, 'z':3, 'a': 4})
# 这种传值方式相当于foo(x=1, y=2, z=3, a=4),如果不是用**,字典就是一个普通的位置形参
各种参数结合使用
以上所有的参数都可以组合使用,但是在定义函数时,参数的顺序必须是:位置参数、默认参数、args、kwargs。
可变长度位置参数(*args
)和可变长度关键字参数(**kwargs
)通常组合使用,表示该函数可以接受任何形式、任意长度的参数。
def func(*args, **kwargs):
print(args)
print(kwargs)