1. 参数值是否可修改
先来看两个基本概念:形参和实参,在定义函数的语句中,两个括号之间的变量通常成为形参,而在函数调用时提供的值成为实参。在函数调用阶段,通常是仅使用参数的值,而不会去改变它,例如:
def example_function(x, y):
return x + y
example_function(3, 5) # 8
那么传入的参数到底能不能改变呢?答案是:部分可改变,部分不可改变,具体得看情况。
- 对于字符串、数字和元祖类型的参数来讲,函数内的修改不会影响函数外的变量值。
def unchanged_parameter(str_parameter: str,
int_parameter: int,
tuple_parameter: tuple):
"""字符串、数字和元祖不可改变"""
str_parameter = "new value"
int_parameter = 0
tuple_parameter = (1, 2)
if __name__ == '__main__':
str_example = "example"
int_example = 1
tuple_example = (0, 1)
unchanged_parameter(str_example, int_example, tuple_example)
print(str_example) # example
print(int_example) # 1
print(tuple_example) # (0, 1)
- 对于可变的数据结构(例如:字典、列表)类型的参数来讲,函数内的修改会影响函数外的变量
def variable_parameter(dict_parameter: dict,
list_parameter: list):
"""可变数据结构类型(如:字典、列表)的参数可改变"""
dict_parameter["new"] = "new value"
list_parameter[0] = 1
if __name__ == '__main__':
### 可变示例
dict_example = {
"old": "old value"
}
list_example = [0, 1, 2, 4]
variable_parameter(dict_example, list_example)
print(dict_example)
print(list_example)
对于不可变参数这种情况,如果的确需要函数在执行后修改对应参数的值,则需要将修改的值返回并重新赋值给这个变量,例如:
def change_int_variable(a):
return a + 2
a = 2
a = change_int_variable(2)
print(a) # 4
对于可变参数这种情况,在函数执行过程中会修改参数的值,但是函数执行过程后依旧想保持参数原来的值,则需要在传参之前添加一个复制的步骤得到原来数据的副本,并将副本作为参数传递给函数。以列表为例:
list_example = [0, 1, 2, 4]
list_example_copied = list_example[:]
def change_list_value(list_parameter):
list_parameter[0] = 1
change_list_value(list_example_copied)
print(list_example_copied) # [1, 1, 2, 4]
print(list_example) # [0, 1, 2, 4]
2. 位置参数和关键字参数
上面的看到的示例,都是位置参数,参数的位置比它们的名字更重要。可以这么理解位置参数:实参和形参的位置要一一对应,如果函数调用时,实参的调用位置发生变化,对函数的执行效果有巨大的影响。
def example_function(a, b):
return a + ", " + b
a = "Hello"
b = "world"
print(example_function(a, b)) # Hello, world
print(example_function(b, a)) # world, Hello
可以看到,对于上面的函数example_function,在调用时,如果参数a和b实参位置发生了变化,最终的执行结果也发生了变化。对于函数参数不多的时候,我们可以记住这些参数的具体的位置,但是一旦函数参数过多,就会带来很差的体验,降低开发效率。这种时候,在调用函数时,可以提供参数的名称,例如:
print(example_function(a=a, b=b)) # Hello, world
print(example_function(b=b, a=a)) # Hello, world
可以看到,改变参数的位置也不会影响函数的执行和输出,但需要注意实参和形参一定要对应。我们称这种参数的使用方式为关键字参数。
3. 参数默认值
带有默认值的参数也称为缺省参数,在函数声明时,为形参提供一个默认值;在函数调用时,对应的实参可传也可不传,传递参数时,会覆盖该参数的默认值,例如:
def example_function(a, b=1):
return a ** b
a = 2
b = 3
print(example_function(a)) # 2
print(example_function(a, b)) # 8
为参数提供默认值时,有一点需要注意:带有默认值的参数,一定要放在参数列表的末尾,无论是函数定义时还是调用函数时,否则会提示错误。
4. *args 和 **kwargs
上面的几个例子,无论是位置参数还是关键字参数,函数的参数个数都是固定的。但在实际过程中,经常会出现函数需要接收任意数量的参数的需求,这种情况下*args和**kwargs就派上了用场。
*args全称为Non-keyword Variable Arguments,无关键字参数,当函数中以列表或者元祖的方式传参时,使用*args。当*args和位置参数一起使用时,优先给位置参数分配参数,再将多余的参数以元祖的形式分配给*args。*args不能和关键字参数一起使用。
示例:
def example_args_function(a, b, *args):
print(a)
print(b)
print(args)
example_args_function(1, 2, 3, "a", [3, 4], (5, 6))
# 1
# 2
# (3, 'a', [3, 4], (5, 6))
**kwargs全称为keyword Variable Arguments,有关键字参数,当以字典的方式传参时,使用**kwargs。**kwargs可以与位置参数、关键词参数一起使用。
示例:
def example_kwargs_function(a, b, **kwargs):
print(a)
print(b)
print(kwargs)
example_kwargs_function(1, 2, c=3, d=4)
# 1
# 2
# {'c': 3, 'd': 4}
example_kwargs_function(1, b=2, c=3, d=4)
# 1
# 2
# {'c': 3, 'd': 4}
- 位置参数、带默认值的位置参数、
*args和**kwargs一起声明式,它们的前后顺序为:位置参数、带默认值的位置参数、带默认值的位置参数、*args和**kwargs。但是,带默认值参数、*args和**kwargs一起使用时,通常情况下都会覆盖这个默认值,所以,一般在和*args、**kwargs一起使用时,会忽略带默认值的位置参数
def example_with_mixed_arguments(a, b=1, *args, **kwargs):
print(a)
print(b)
print(args)
print(kwargs)
example_with_mixed_arguments(1)
# 1
# 1
#
# {}
example_with_mixed_arguments(1, 2)
# 1
# 2
#
# {}
example_with_mixed_arguments(1, 2, 3, 4)
# 1
# 2
# (3, 4)
# {}
example_with_mixed_arguments(1, 2, 3, 4, c=5, d=6)
# 1
# 2
# (3, 4)
# {'c'=5, 'd'=6}
example_with_mixed_arguments(1, 2, 3, 4, b=7, c=5, d=6)
# TypeError: example_with_mixed_arguments() got multiple values for argument 'b'
example_with_mixed_arguments(1, 2, 3, 4, c=5, d=6)
# 如果想让b保留默认值,把2、3、4赋值给args,这是不可实现的。
5. *args和**kwagrs的逆操作
上一小节讲述了*args和**kwargs的“正向”操作:函数在定义时,使用*和**表示任意数量参数的收集,*用元祖的方式收集不匹配的位置参数,**用字典的形式收集关键字参数。所谓逆操作或者反操作,是指在函数调用时实参中使用*和**,这时是用来解包参数的,*将元祖或者列表解包为位置参数,相对应的,**将字典解包为关键字参数。例如:
def example_reverse_function(a, b):
print(a ** b)
a = 2
b = 3
example_reverse_function(*(a, b))
# 8
c = {
'a': a,
'b': b
}
example_reverse_function(**c)
# 8