Python高级之函数介绍
函数的基本介绍
【一】函数的特点以及使用情形
【1】函数的特点:
- 封装(Encapsulation):
- 函数封装了一段特定的代码,将其作为一个独立的单元。这有助于提高代码的可维护性和可读性。
- 重用性(Reusability):
- 函数可以在程序中的不同位置被调用,实现了代码的重用。通过调用函数,可以避免重复编写相同的代码。
- 参数(Parameters):
- 函数可以接受输入参数,这样可以在函数内部使用外部提供的数据。参数使得函数更加通用,适用于不同的输入。
- 返回值(Return Value):
- 函数可以返回结果,这样调用者可以获取函数的输出。返回值是函数向外部提供结果的一种方式。
- 模块化(Modularity):
- 函数可以作为模块,通过函数的组合可以构建复杂的程序。模块化使得代码结构更清晰,易于理解和维护。
- 局部变量(Local Variables):
- 函数内部可以定义局部变量,这些变量的作用范围仅限于函数内部。这有助于避免命名冲突和提高代码的安全性。
【2】使用情形:
- 代码复用:
- 当某段代码需要在程序的不同部分被重复使用时,将其封装成函数,可以通过函数调用来实现代码的复用。
- 抽象操作:
- 函数提供了对某个操作的抽象,使得调用者无需关心操作的具体实现细节。这样可以提高代码的抽象层次,简化调用者的逻辑。
- 模块化设计:
- 大型程序通常由许多小模块组成,每个模块可以是一个函数。通过模块化设计,可以更容易地理解和维护代码。
- 参数化操作:
- 函数可以接受参数,使得操作变得更加通用。通过参数传递不同的值,可以在不同的上下文中使用同一个函数。
- 组织结构:
- 函数有助于组织程序的结构。将不同功能的代码封装在不同的函数中,使得程序更加有序,易于理解。
- 提高可读性:
- 使用函数可以将复杂的逻辑分解成简单的步骤,提高了代码的可读性。每个函数都承担特定的责任,使得代码更具清晰度。
- 测试和调试:
- 函数可以更容易地进行单元测试,因为函数是独立的单元。在调试时,函数也提供了更容易定位问题的标志点。
【二】函数的定义与调用
【1】定义的语法
(引)基本概念
- 定义函数的语法
def 函数名(参数1,参数2,...):
"""文档描述"""
函数体
return 值
- def: 定义函数的关键字;
- 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
- 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
- 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
- """文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
- 函数体:由语句和表达式组成;
- return 值:定义函数的返回值,return是可有可无的。
(1)空函数
def 函数名():
.../pass
- 一般在开始结构构建阶段,作为标记使用
(2)无参无返回值
def 函数名():
函数体
'''例如'''
def add():
print(1+1)
- 一般是进行一些操作且不需要返回值时使用
(3)有参无返回值
def 函数名(参数1,参数2,...):
函数体
'''例如'''
def add(x,y):
print(x+y)
(4)有参有返回值
def 函数名(参数1,参数2,...):
函数体
return 数据
'''例如'''
def add(x,y):
msg=x+y
return msg
【2】函数的调用方式
(1)直接调用
def add(x,y):
return x+y
print(add(1,2))
- 通过
函数名+()
直接进行调用
(2)表达式调用
def add(x,y):
return x+y
result = add # add = add() # add作为变量名,add()函数作为内存地址
print(result(1,2)) # result=add # 为result赋值add # add作为add()的变量名其对应的值为函数的内存地址
# 故 result() == add() # 所以给result传参执行的就是add()函数的操作,并返回1+2的值3
(3)函数作为参数
def add():
return 1+9 # add()函数返回1+9的值10
def mix(x,y):
return x*y
print(mix(x=add(),y=10)) # 100 # mix()需要的两个参数x,y # x = add() = 10, y = 10
【三】函数的参数
【1】函数的参数类型[形参&实参]
(1)形参(形式参数)
- 形参即在定义函数时,括号内声明的参数。
- 形参本质就是一个变量名,用来接收外部传来的值。
(2)实参(实际参数)
- 实参即在调用函数时,括号内传入的值
- 值可以是常量、变量、表达式或三者的组合
def add(形参1,形参2):
return 实参1+实参2
add(形参1=实参1,形参2=实参2) # 实参为数值,形参为关键字
(3)参数类型弱警告
- 可以为形参设定期望参数
- 通过
函数名(参数1:数据类型,参数2)
实现 - 弱警告并不会报错,只是警示开发人员,此处希望得到的数据类型是什么
- 通过
# 可以通过在参数后加上冒号并指定参数类型
def add(x: int, y):
return x + y
add('a', 2)
【2】传参数
(1)位置传参
- 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参
- 顺序错误将导致传递数值错误
- 按位置传参数据
数量
必须一致,否则将导致报错
def add(a,b,c):
return a+b+c
print(add('我','爱','你')) # 我爱你
# 正常按位置传参数 输出内容为 我爱你
print(add('爱','我' '你')) # 爱我你
# 位置与参数位置不一致,将导致输出内容错乱
print(add('我', '爱', '你','吗?')) # add() takes 3 positional arguments but 4 were given
# 参数多了一个
(2)关键字传参
-
在调用函数时,实参可以是key=value的形式,称为关键字参数
- 凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
def add(a, b, c):
return a + b + c
print(add(a='我', b='爱', c='你')) # 我爱你
print(add(b='爱', a='我', c='你')) # 我爱你
# 不管关键字参数位置怎么变,都是我爱你,爱你哟~
(3)位置参数和关键字参数混合使用
- 需要注意,关键字参数不可以在位置参数前,否则将报错
def add(a, b, c):
return a + b + c
print(add('I', b='love', c='you')) # Iloveyou
# ’I'是位置参数,'love''you'是关键字参数
print(add(a='I', 'love', c='you')) # SyntaxError: positional argument follows keyword argument
# 当关键字参数在前,后续就 不可以出现位置参数
# 'I'作为关键字参数传入后,'love'就必须指定关键字成为关键字参数,否则将报错
(4)默认参数
- 在定义函数时,就已经为形参赋值,这类形参称之为默认参数
- 当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。
# 默认参数类似于实参中的关键字参数,故需要放在参数的最后面
def add(a,c,b='love'):
return a+b+c
print(add('我','你')) # 我love你
# 这里需要先用位置传参,a='我',c='你',b作为默认参数,可以不用传参
# 默认参数'love'无需多言
print(add('我','你','爱')) # 我爱你
# 默认参数同样是可以传参的,如果传参就将替换掉默认参数的值
# 其他情况
# 当设定了命名关键字参数时,'*'后的所有参数都需要使用命名关键字,而这时,默认参数的位置就不会限制在最后面了
def add(a, *, b='love', c):
return a + b + c
print(add('I', b='love', c='you')) # Iloveyou
【3】可变长参数
(1)可变长位置参数*args
- 如果在最后一个形参名前加
*
号,那么在调用函数时,溢出的位置实参,都会被*
接收,以元组的形式保存下来赋值给该形参 - 既然时元组就可以进行解包赋值的操作
# 在最后一个形参名args前加*号
def foo(x, y, z=1, *args):
print(x)
print(y)
print(z)
print(args)
foo(1, 2, 3, 4, 5, 6, 7)
# 实参1、2、3按位置为形参x、y、z赋值
# 多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args
# 即args=(4, 5, 6,7)
# 可变长位置参数可以求多个值的和
def add(*args):
res = 0
for i in args:
res += i
return res
res = add(1, 2, 3, 4, 5)
print(res) # 15
# 解包
def foo(x, y, *args):
print(x)
print(y)
print(args)
L = [3, 4, 5]
foo(1, 2, *L)
# *L就相当于位置参数3,4,5 ----> 解包
# 对应到函数中就是,foo(1,2,*L)就等同于foo(1,2,3,4,5)
(2)可变长关键字参数**kwargs
- 如果在最后一个形参名前加
**
号,那么在调用函数时,溢出的关键字参数,都会被**
接收,以字典的形式保存下来赋值给该形参
def foo(x, **kwargs): # 在最后一个参数kwargs前加**
print(x)
print(kwargs)
foo(y=2, x=1, z=3)
# 溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
# 1
# {'y': 2, 'z': 3}
def foo(x, y, **kwargs):
print(x)
print(y)
print(kwargs)
dic = {'a': 1, 'b': 2}
foo(1, 2, **dic)
# **dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)
# 等同foo(1,2,a=1,b=2)
# 1
# 2
# {'a': 1, 'b': 2}
【4】命名关键字参数*
-
想要限定函数的调用者必须以key=value的形式传值
-
需要在定义形参时,用
*
作为一个分隔符号,*
号之后的形参称为命名关键字参数。 -
对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值
def register(name, age, *, sex, height): # sex,height为命名关键字参数
pass
# 正确使用
register('user', 18, sex='male', height='1.9m')
# TypeError:未使用关键字的形式为sex和height传值
register('user', 18, 'male', '1.9m')
# TypeError没有为命名关键字参数height传值。
register('user', 18, height='1.9m')
【5】混合使用
- 综上所述所有参数可任意组合使用,但定义顺序必须是:
位置参数
、默认参数
、*args
、命名关键字参数
、**kwargs
- 可变参数
*args
与关键字参数**kwargs
通常是组合在一起使用的 - 如果一个函数的形参为
**args
与**kwargs
,那么代表该函数可以接收任何形式、任意长度的参数
def wrapper(*args,**kwargs):
pass
- 在该函数内部还可以把接收到的参数传给另外一个函数(这在装饰器的实现中大有用处)
def func(x, y, z):
print(x, y, z)
def wrapper(*args, **kwargs):
func(*args, **kwargs)
wrapper(1, z=3, y=2)
# 1 2 3
- 提示:
*args
、**kwargs
中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。