Python-函数定义详解

函数定义支持可变数量的参数。下面列出三种可以组合使用的形式。

1 默认值参数

为参数指定默认值,在调用函数时可以使用比定义时更少的参数。

def student_info(name, sex, age=18, score=100):
    if sex == 'male':
        print(f"{name} is {age} years old, his score is {score}")
    else:
        print(f"{name} is {age} years old, her score is {score}")

该函数接收两个必选参数namesex ,两个可选参数 agescore 。该函数可用下列方式调用

  • 只传入必选参数
student_info('ZhangSan', 'male')
# zhangSan is 18 years old, his score is 100.
  • 传入一个可选参数
student_info('LiSi', 'female', 17)
# LiSi is 17 years old, her score is 100.
  • 传入所有实参
student_info('XiaoHong', 'female', 17, 99)
# XiaoHong is 17 years old, her score is 99.

2 关键字参数

kwarg=value 形式的 关键字参数 也可以用于调用函数。

def student_info(name, sex, age=18, score=100):
    if sex == 'male':
        print(f"{name} is {age} years old, his score is {score}")
    else:
        print(f"{name} is {age} years old, her score is {score}")

使用了默认值参数的函数都可以通过kwarg=value 形式的关键字参数调用函数。该函数可以通过下列方式调用。

student_info('ZhangSan', 'male')
student_info('ZhangSan', 'male', age=17)
student_info('ZhangSan', 'male', score=99)
student_info('ZhangSan', 'male', age=19, score=98)
student_info('ZhangSan', 'male', score=95, age=20)

从上面调用函数的方式可以看出:

  • 关键字参数的顺序可以和定义的顺序不一致
  • 关键字参数必须放在位置参数之后

如果你想打乱所有参数的顺序,包括位置参数,那么你需要对所有参数使用kwarg=value 的方式来传入实参。如:

student_info(score=100, age=19, name='ZhangSan', sex='malae')

不能在一次调用中多次对同一个参数进行赋值(即使多次赋值都是同一个值):

student_info('ZhangSan', 'male', name='ZhangSan')

这样是不被允许的。

**name

当最后一个形参为**name 形式时,接收一个字典,该字典包含与函数中已定义形参之外的所有关键字参数。**name可以和*name形参组合使用(注意:*name必须在**name前面),*name接收一个元组,该元组包含形参列表之外的位置参数。

其中**name接收的字典中数据的顺序和调用函数时实参传入的顺序一致。

3 特殊参数

默认情况下,参数可以按位置或显式关键字传递给 Python 函数。为了让代码易读、高效,最好限制参数的传递方式,这样,开发者只需查看函数定义,即可确定参数项是仅按位置、按位置或关键字,还是仅按关键字传递。需要使用/*

函数定义如下:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    -----------    ----------
           |              |             |
           |              |             |
        位置参数    位置参数或关键字参数  关键字参数

其中/* 是可选的。

3.1 位置或关键字参数

当函数定义中未使用/*时,参数可以按照位置或关键字传递给函数。

3.2 仅位置参数

特定形参可以标记为 仅限位置仅限位置 时,形参的顺序很重要,且这些形参不能用关键字传递。仅限位置形参应放在 / (正斜杠)前。/ 用于在逻辑上分割仅限位置形参与其它形参。如果函数定义中没有 /,则表示没有仅限位置形参。

/ 后可以是 位置或关键字仅限关键字 形参。

3.3 仅限关键字参数

把形参标记为仅限关键字,表明必须以关键字参数的形式传递该参数。

3.4 函数示例

def standard_arg(arg):
  	print(arg)

def pos_only_arg(arg, /):
  	print(arg)

def kwd_only_arg(*, arg):
  	print(arg)
    
def combined_example(pos_only, /, standard, *, kwd_only):
  	print(pos_only, standard, kwd_only)

第一个函数 standard_arg 的函数定义对调用的方式没有任何限制,可以按位置也可以按关键字传递参数:

standard_aeg(2)
standard_aeg(arg=2)

第二个函数 pos_only_arg 的函数定义中有 / ,且形参全在 / 之前定义,所以仅限使用位置参数。

pos_only_arg(3)
pos_only_arg(arg=3) # 无效的调用
# TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

第三个函数 kwd_only_arg 的函数定义通过 * 表明仅限关键字参数。

kwd_only_arg(arg=5)
kwd_only_arg(5) # 无效的调用
# TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

最后一个函数 combined_example 的函数定义中,使用了全部三种调用惯例:

  • 该函数接收三个参数
  • 第一个参数仅限位置参数
  • 第二个参数可使用位置或关键字参数
  • 第三个参数仅限关键字参数
combined_example(6, 7, kwd_only=8)
combined_example(6, standard=7, kwd_only=8)

下面的函数定义中,kwds 里面不能把 name 当做键。会与第一个参数 name 冲突。

def foo(name, **kwds):
  	print(name, **kwds)
    
foo(1, **{name: 2})

因为上面的函数调用相当于把name 赋值了两次。等价于:

foo(1, name=2)

由于此时name 既可以当作位置参数,也可以当作关键字参数,所以上述行为相当于给一个参数在一次函数调用中赋值了两次。

那么如何才能让 kwds 使用 name 当作键呢?

这就需要使用 特殊参数 / 来解决了,只要把 name 参数仅限使用位置参数,那么再使用name=2 时就不会被认为是第二次赋值了。

所以需要把函数定义为:

def foo(name, /, **kwds):
  	print(name, kwds)
    
foo(1, name=2, age=18)
# 1 {'name': 2, 'age': 18}

就可以了。👼

总结:仅限位置形参的名称可以在**kwds中使用,而不产生歧义。

4 任意实参列表

调用函数时,使用任意数量的实参是最少见的选项。这些实参包含在元组中。

有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种 情况下,可将函数编写成能够接受任意数量的键值对——调用语句提供了多少就接受多少。

def sumnum(a, *ddd):
    print(a)
    result = a
    for num in ddd:
        result += num
    return result
print(sumnum(1, 2, 3, 5, 6, 9, 8, 7))
# 1
# 41

*name 用于接收传递给函数的所有剩余位置参数,因此*name 的后面不允许出现位置参数,而是仅限关键字参数,同样的*name 的前面也不允许出现任何的关键字参数,而是仅限位置参数。

5 解包实参列表

函数调用要求独立的位置参数,但实参在列表或元组里时,要执行相反的操作。例如,内置的 range() 函数要求独立的 start 和 stop 实参。如果这些参数不是独立的,则要在调用函数时,用 * 操作符把实参从列表或元组解包出来:

>>> list(range(3, 6))         
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args))         
[3, 4, 5]

同样,字典可以用 ** 操作符传递关键字参数。

6 Lambda 表达式

Lambda 关键字用于创建小巧的匿名函数。

Lambda a, b: a+b

上面的函数返回两个参数的和。Lambda 函数可用于任何需要函数对象的地方。在语法上,匿名函数只能是单个表达式。在语义上,它只是常规函数定义的语法糖。与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量:

def make_incrementor(n):
    return lambda x: x + n

f = make_incrementor(42)
print(f(0))
# 42
print(f(1))
# 43

上例用 lambda 表达式返回函数。还可以把匿名函数用作传递的实参:

pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pa: pa[1])
print(pairs)
# [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
posted @ 2021-09-09 16:52  雨-铃  阅读(601)  评论(0编辑  收藏  举报
Tips
复制成功!