先记录一下函数的两种定义方法:
1 使用def关键字,代码示例:
def add(x, y):
return x + y
# 调用函数
add(1, 2)
2 使用lambda关键字定义一个匿名函数,代码示例:
add = lambda x, y: x + y
print(add(1, 2))
参数(parameter)是函数的重要组成部分,它是函数最主要的输入源,决定了调用方使用函数时的体验。
在定义函数时有一些小技巧:
1 别将可变类型作为参数默认值
def append_value(value,items=[]):
items.append(value)
return items
print(append_value(1)) # [1]
print(append_value(2)) #[1,2]
怪事发生了。第二次调用的时候,没有返回预期的[2],而是返回了[1,2]。这意味着,第二次调用函数时,items参数值不再是[],而是第一次调用函数的结果[1]。
这是因为Python 函数的参数默认值只会在函数定义阶段被创建一次,之后不论再调用多少次,函数内拿到的默认值都是同一个对象。
为了规避这个问题,使用None来替代可变类型默认值是比较常见的做法:
def append_value(value, items=None):
if items is None:
items = []
items.append(value)
return items
print(append_value(1)) # [1]
print(append_value(2)) # [2]
但是这种做法有一个问题,就是程序无法判断None是默认值还是调用时主动传入的参数值。
print(append_value(value=1))
print(append_value(value=1,items=None))
要解决这个问题,最常见的做法是定义一个特殊对象(标记变量)作列参数默认值。
# object通常不会单独使用,但是拿来做这种标记变量刚刚好
_not_set = object()
def append_value(value, items=_not_set):
if items is _not_set:
#如果调用方没有传入items参数
...
相比 None, _not_set 是一个独一无二、无法随意获取的标记值。假如函数在执行时判断extra的值等于 _not_set,那我们基本可以认定:调用方没有提供 items参数。
2 定义仅限关键字参数
当一个函数定义了超多参数,调用函数时又没有指定参数名字时,代码的可读性就大大降低了,使用关键字参数模式可以大大提高代码的可读性。
def query_users(limit, offset, *, min_followers_count, include_profile):
pass
通过在参数列表中插入 *符号,该符号后的所有参数都变成了“仅限关键字参数”(keywordonly argument)。如果调用方仍然想用位置参数来提供这些参数值,程序就会抛出错误:
正确的调用方式:
query_users(20,0,min_followers_count=100, include_profile=True)
3 可变数量参数
参数:*args。函数调用的时候是将传入的所有参数组装成元组传入。
def func1(*args):
s = 0
print(args) # (1,2,3,4)
for i in args:
s = s+i
return s
print(func1(1,2,3,4)) # 10
调用的时候如果 可缺省参数没有指定的时候,除了必传参数有值以外,其他的值都是可变参数的值,并且会将可变参数自动封装成元组
def func2(a,*args,b=1):
print(a)
print(*args)
print(b)
func2(1,2,3,4,5) #a=1,*args=(2,3,4,5),b=1
func2(1,2,3,4,b=5)#a=1,*args=(2,3,4),b=5
4 关键字可变数量参数
**kwargs 在函数定义中是一个常见的约定,用于收集传入函数中所有未明确指定的关键字参数,并将它们存储在一个字典中。这个字典的名字通常是 kwargs。
def my_function(arg1, arg2, **kwargs):
print(f"arg1: {arg1}")
print(f"arg2: {arg2}")
for key, value in kwargs.items():
print(f"{key}: {value}")
# 调用函数
my_function(1, 2, keyword1="value1", keyword2="value2")
输出:
arg1: 1
arg2: 2
keyword1: value1
keyword2: value2
注意:
1 *args 和 **kwargs 经常一起出现,但它们的用途不同。*args 用于收集非关键字参数(即位置参数),并将它们存储在一个元组中。
2 使用 **kwargs 可以使函数更加灵活,因为它可以接受任意数量的关键字参数。
3 由于 kwargs 是一个字典,因此访问其值时需要使用键来获取。
4 在定义函数时,*args 必须出现在 **kwargs 之前(如果有的话),因为位置参数必须在关键字参数之前。
5 在使用 **kwargs 时,需要确保传递给函数的关键字参数是预期的,并且不会导致函数内部的逻辑错误。