本次我要给大家分享一些更加高级和不常见的函数定义与使用模式。
涉及到的内容包括默认参数、任意数量参数、强制关键字 参数、注解和闭包等。
*
表达式
我们知道在定义函数的时候,用 *
表达式来收集所有未指明的位置参数。
def avg(first, *rest):
"""求平均值"""
return (first + sum(rest)) / (1 + len(rest))
# 简单使用
avg(1, 2) # 1.5
avg(1, 2, 3, 4) # 2.5
当然在调用函数的时候,也可以使用 *
表达式来把一个序列类型数据中的元素一一解开。
def show_args(*args):
print(args)
show_args(['a','b']) # (['a', 'b'],)
show_args(*['a','b']) # ('a', 'b')
print()
函数的另外一种用法
print(['a','b'], sep='\n')
# ['a', 'b']
print(*['a','b'], sep='\n')
# a
# b
print(*'ab', sep='\n')
# a
# b
其实 print()
函数的原型是这样定义的
def print(self, *args, sep=' ', end='\n', file=None):
pass
print()
函数中的sep
关键字参数定义的是当打印多个参数时,它们中间的分隔符是什么
**
表达式
def foo(**kwargs):
print(kwargs) # 是一个字典
一个
*
参数只能出现在函数定义中最后一个位置参数的后面,而**
参数只能出现在最后一个参数都位置。
有一点要注意的是,在*
参数后面其实还可以定义其他参数。下面就会用到。
在函数中实现强制关键字参数
有的时候你希望在调用函数的时候,必须用关键字参数,因为这样更易懂。
可以将要限定的强制关键字参数放到某个 *参数
或者单个 *
后面就能达到这种效果。
像下面这样。
def query_keyword(max_file, *, servers):
pass
query_keyword(65535, servers=False) # ok
query_keyword(65535) # TypeError
使用函数注解
函数注解是为了让看这个函数源码的人更能清楚参数的类型和用法
def add(x:int, y:int) -> int:
return x + y
python
解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟 没有加注解之前的效果也没有任何差距。
这些注解存储在函数的
__annotations__
属性中。
print(add.__annotations__)
感受 return
的强大
def foo():
return '千锋', 8, 1000000
name, *nums = foo()
print(name) # 千锋
print(nums) # [8, 1000]
其实在函数返回之前,先创建了一个元组,之后的赋值就是我们之前讲的元组解包
在定义函数的默认参数时,不要用可变类型的数据
假如你的确需要一个默认参数是一个可边类型的数据(比如列表)
可以把默认参数的值先定义为 None
def foo(a, b=None):
if b is None:
b = []
验证同一性
假如你想在一个函数中判断使用者有没有给一个参数传参,你可能想到这样:
def spam(a, b=None)
if not b: # 判断是否是 False
print("用户没有传递变量")
这样显然会有问题的,因为对于 python
来说, 长度为 0 的字符串 ''
、列表 []
、元组 ()
、字典 {}
都会认为是 False
, 并且数字 0
和 布尔值的 False
都认为是假的。
也就是说用户传入这些参数是属于合法的参数。
解决办法:
_no_value = object()
def spam(a, b=_no_value):
if b is _no_value:
print("用户没有传递变量")
_no_value
是object()
的是个实例, 这样可以判断变量b
的值和_no_value
的值是否是同一个对象来判断用户是否传入了值。
匿名函数
在匿名函数中使用了一个变量的值,这会是很有意思的一件事。
li = [lambda n=30: n for n in range(10)]
现在你回答下面几个问题
-
li[0]
是什么类型的对象?
li
是一个列表,其中的元素都是匿名函数
-
li[0]()
和li[1]()
分别都是什么值?
其实
li[0]()
和li[1]()
的值都是9
lambda
表达式中的n
是一个自由变量,是在运行时给其绑定值,而不是在定义时就给其绑定值,这跟函数的默认值参数定义是不同的。
因此,在调用这个lambda
表达式的时候,n
的值是执行时的值。
还有要考虑到, 在 Python 中只有在函数中定义的变量才是局部变量,其他都是全局的变量。for
循环到最后n
的值被绑定为9
, 所以li
所用函数中的n
的值在运行时都是9
。
现在对 lambda
函数稍作修改就会有不一样的效果
funcs = [lambda n=n: n for n in range(10)]
funcs[0]() # 0
funcs[1]() # 1
等号左边的
n
是函数的形参,右边是函数的实参,实参也就是迭代的变量的值。
这里利用默认参数,就可以在定义函数时,把值绑定给变量。
未完, 待续...
网友评论