视频加载失败

Python高级之函数的参数

2958 字
15 分钟
Python高级之函数的参数

【一】形参和实参的介绍#

  • 函数的参数分为形式参数和实际参数,简称形参和实参
    • 形参即在定义函数时,括号内声明的参数。
      • 形参本质就是一个变量名,用来接收外部传来的值。
    • 实参即在调用函数时,括号内传入的值
      • 值可以是常量、变量、表达式或三者的组合:
def add(x, y):
return x + y
# 1:实参是常量
res_first = add(1, 2)
print(res_first) # 3
# 2:实参是变量
x = 5
y = 6
res_second = add(x, y)
print(res_second) # 11
# 3:实参是表达式
res_third = add(10 * 2, 10 * add(3, 4))
print(res_third) # 90
# 4:实参可以是常量、变量、表达式的任意组合
a = 2
res_forth = add(a, 10 * add(3, 4))
print(res_forth) # 72
  • 在调用有参函数时,实参(值)会赋值给形参(变量名)。
  • 在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。

【二】位置参数#

  • 位置即顺序,位置参数指的是按顺序定义的参数
  • 需要从两个角度去看:
    • 在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参
    • 凡是按照这种形式定义的形参都必须被传值

【1】有位置参数而不传值会报错#

# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register() # TypeError:缺少3个位置参数
'''
Traceback (most recent call last):
File "E:\PythonProjects\01.py", line 175, in <module>
register() # TypeError:缺少3个位置参数
TypeError: register() missing 3 required positional arguments: 'name', 'age', and 'sex'
'''

【2】不按位置传参会导致变量错误#

# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register(18, "男", "Dream")
# Name:18 Age:男 Sex:Dream

【3】对应位置传参#

# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register("Dream", 18, "男")
# Name:Dream Age:18 Sex:男

【4】小结#

  • 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参
  • 凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应

【三】关键字参数#

在调用函数时,实参可以是key=value的形式,称为关键字参数

  • 凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值

【1】按顺序传参#

# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 根据指定关键字进行传参,不能多传参数,也不能少传参数
register(name="Dream", age=18, sex="男")
# Name:Dream Age:18 Sex:男

【2】不按顺序传参#

# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 根据指定关键字进行传参,可以不按顺序传参
register(sex="男", name="Dream", age=18)
# Name:Dream Age:18 Sex:男

【3】位置和关键字混合使用#

  • 需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用
  • 但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值
# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 可以使用关键字和位置参数混合传参,但是一定要位置参数在前,关键字参数在后
register("Dream", age=18, sex="男")
# Name:Dream Age:18 Sex:男

【四】默认参数#

  • 在定义函数时,就已经为形参赋值,这类形参称之为默认参数
    • 当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。
    • 例如编写一个注册学生信息的函数
    • 如果大多数学生的性别都为男,那完全可以将形参sex定义成默认参数

【1】默认参数不传值#

# 定义位置形参:name,age 必须被传值 sex默认值为 男
def register(name, age, sex="男"):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 可以使用关键字和位置参数混合传参,但是一定要位置参数在前,关键字参数在后
# 参数有默认值,可以不传值
register("Dream", age=18)
# Name:Dream Age:18 Sex:男

【2】默认参数传值#

(1)默认参数初始化#

  • 默认参数必须在位置参数之后
  • 默认参数的值仅在函数定义阶段被赋值一次
# 定义位置形参:name,age 必须被传值 sex默认值为 男
def register(name, age, sex="男"):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 可以使用关键字和位置参数混合传参,但是一定要位置参数在前,关键字参数在后
# 参数有默认值,重复传值会覆盖掉默认值
register("Dream", age=18, sex="女")
# Name:Dream Age:18 Sex:女

(2)默认参数为可变数据类型#

  • 默认参数的值通常应设为不可变类型
  • 每次调用是在上一次的基础上向同一列表增加值
def append_list(num, list_inner=[]):
list_inner.append(num)
return list_inner
# 传入参数 1 ,对应在函数中的空列表参数中添加值
res_1 = append_list(1)
print(res_1) # [1]
# 由于列表是可变数据类型,所以在上面的基础上,再添加值会添加到上一个列表内
res_2 = append_list(2)
print(res_2) # [1, 2]
# 同理
res_3 = append_list(3)
print(res_3) # [1, 2, 3]

(3)默认参数为空#

  • 默认参数的值通常应设为不可变类型
def append_list(num, list_inner=None):
if list_inner is None:
list_inner = []
list_inner.append(num)
return list_inner
# 传入参数 1 ,对应在函数中的参数为空
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_1 = append_list(1)
print(res_1) # [1]
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_2 = append_list(2)
print(res_2) # [2]
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_3 = append_list(3)
print(res_3) # [3]

【五】可变长参数#

  • 参数的长度可变指的是在调用函数时,实参的个数可以不固定
    • 而在调用函数时,实参的定义无非是按位置或者按关键字两种形式
    • 这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数

【1】可变长位置参数(*args)#

(1)任意类型参数#

  • 如果在最后一个形参名前加 * 号,那么在调用函数时,溢出的位置实参,都会被 * 接收,以元组的形式保存下来赋值给该形参
# 在最后一个形参名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 foo(x, y, z=3):
print(x)
print(y)
print(z)
foo(*[1, 2]) # 等同于foo(1,2)
  • 如果我们想要求多个值的和,*args 就派上用场了
def add(*args):
res = 0
for i in args:
res += i
return res
res = add(1, 2, 3, 4, 5)
print(res) # 15

(2)解包#

  • 如果我们事先生成了一个列表,仍然是可以传值给 *args 的
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)
  • 注意:如果在传入 L 时没有加 * ,那 L 就只是一个普通的位置参数了
def foo(x, y, *args):
print(x)
print(y)
print(args)
L = [3, 4, 5]
foo(1, 2, L)
# 如果在传入 L 时没有加 `*` ,那 L 就只是一个普通的位置参数了
# ([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}
  • 如果我们事先生成了一个字典,仍然是可以传值给 **kwargs 的
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}
  • 如果在传入 dic 时没有加 ** ,那 dic 就只是一个普通的位置参数了
def foo(x, y, **kwargs):
print(x)
print(y)
print(kwargs)
dic = {'a': 1, 'b': 2}
foo(1, 2, dic)
'''
Traceback (most recent call last):
File "E:\PythonProjects\01.py", line 358, in <module>
foo(1, 2, dic)
TypeError: foo() takes 2 positional arguments but 3 were given
'''
  • 如果形参为常规参数(位置或默认),实参仍可以是 ** 的形式
def foo(x, y, z=3):
print(x)
print(y)
print(z)
foo(**{'x': 1, 'y': 2}) # 等同于foo(y=2,x=1)

【3】组合使用#

def register(name, age, *args, **kwargs):
user_dict = {'name': name, 'age': age}
if args:
user_dict['additional_info'] = args
if kwargs:
user_dict.update(kwargs)
return user_dict
user_dict = register("Dream", 18, "handsome", hobby=["music"])
print(user_dict)
# {'name': 'Dream', 'age': 18, 'additional_info': ('handsome',), 'hobby': ['music']}

【六】命名关键字参数#

【1】函数内判断#

  • 在定义了**kwargs参数后,函数调用者就可以传入任意的关键字参数key=value
  • 如果函数体代码的执行需要依赖某个key,必须在函数内进行判断
def register(name, age, **kwargs):
if 'sex' in kwargs:
# 有sex参数
pass
if 'height' in kwargs:
# 有height参数
pass

【2】命名关键字参数#

  • 想要限定函数的调用者必须以key=value的形式传值
    • Python3提供了专门的语法:+
      • 需要在定义形参时,用 * 作为一个分隔符号,* 号之后的形参称为命名关键字参数。
    • 对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值
def register(name, age, *, sex, height): # sex,height为命名关键字参数
pass
# 正确使用
register('Dream', 18, sex='male', height='1.8m')
# TypeError:未使用关键字的形式为sex和height传值
register('Dream', 18, 'male', '1.8m')
# TypeError没有为命名关键字参数height传值。
register('Dream', 18, height='1.8m')

【3】命名关键字参数默认值#

  • 命名关键字参数也可以有默认值,从而简化调用
def register(name, age, *, sex='male', height):
print('Name:%s,Age:%s,Sex:%s,Height:%s' % (name, age, sex, height))
register('Dream', 18, height='1.8m')
# Name:Dream,Age:18,Sex:male,Height:1.8m
  • 需要强调的是:sex不是默认参数,height也不是位置参数
    • 因为二者均在*后,所以都是命名关键字参数,形参sex=’male’属于命名关键字参数的默认值,因而即便是放到形参height之前也不会有问题。
    • 另外,如果形参中已经有一个*args了,命名关键字参数就不再需要一个单独的 * 作为分隔符号了
def register(name, age, *args, sex='male', height):
print('Name:%s,Age:%s,Args:%s,Sex:%s,Height:%s' % (name, age, args, sex, height))
# sex与height仍为命名关键字参数
register('Dream', 18, 1, 2, 3, height='1.8m')
# Name:Dream,Age:18,Args:(1, 2, 3),Sex:male,Height:1.8m

【七】混合使用#

  • 综上所述所有参数可任意组合使用,但定义顺序必须是:
    • 位置参数**、默认参数、*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是约定俗成的。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Profile Image of the Author
Dream
Hello, I'm Dream.
公告
欢迎来到我的博客!爱学习爱进步。
分类
标签
最新动态
站点统计
文章
300
分类
5
标签
26
总字数
640,882
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录