超详细!少数人才知道的 Python 函数定义详解

Python 函数定义时支持可变数量的参数。

一、默认值参数

在 Python 中给函数的参数指定默认值是一种十分常见的使用方式,这样在调用函数时,可以使用比定义时更少的参数。

示例代码

  1. def chat_request(response, retries=4, reminder='Please try again!'):

  2.     while True:

  3.         response = input(response)

  4.         if response in ('y', 'yes'):

  5.             return True

  6.         if response in ('n', 'no'):

  7.             return False

  8.         retries = retries - 1

  9.         if retries < 0:

  10.             raise ValueError('invalid user response')

  11.         print(reminder)

示例代码中定义的函数chat_request可以使用以下三种调用方式:

只给出必选实参response

chat_request('hello world!')

给出一个可选实参retries

chat_request('please say no or yes', 2)

给出所有实参response, retries, reminder

chat_request('please input your mind', 2, 'Dear Baby, but only yes or no!')

默认值在 定义 作用域里的函数定义中求值,为理解这句话的含义,请看如下示例。

Input:

  1. name = 'tony'

  2. def get_name(arg=name):

  3.     print(arg)

  4. name = 'lily'

  5. get_name()

Output:

tony

PS:  默认值只计算一次,默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。

例如,下面的get_num函数会累积后续调用时传递的参数,可以发现输出结果与预期不一致。

Input:

  1. def get_num(num, list1=[]):

  2.     list1.append(num)

  3.     return list1

  4. print(get_num(100))

  5. print(get_num(200))

  6. print(get_num(300))

Output:

  1. [100]

  2. [100, 200]

  3. [100, 200, 300]

PS:  如果不想在后续调用之间共享默认值时,可采取以下方式改写get_num函数

Input:

  1. def get_num(num, list1=None):

  2.     if list1 is None:

  3.         list1 = []

  4.     list1.append(num)

  5.     return list1

  6.     

  7. print(get_num(100))

  8. print(get_num(200))

  9. print(get_num(300))

Output:

  1. [100]

  2. [200]

  3. [300]

二、关键字参数

在 Python 中也可以使用kwarg=value形式的关键字参数来调用函数。

示例代码

  1. def animal(name, state='active', action='fly', type='land'):

  2.     print("This animal wouldn't", action, end=' ')

  3.     print("if you reminder", name, "you are great.")

  4.     print("-- general animal, the", type)

  5.     print("-- They're", state, "!")

代码参数说明

animal函数接受一个必选参数(name)和三个可选参数(state, action 和 type)。

该函数有以下几种调用方式。

  1. animal('jingmao')                                   # 1 positional argument

  2. animal(name='keji')                                 # 1 keyword argument

  3. animal(name='chaiquan', action='running')           # 2 keyword arguments

  4. animal(action='sleep', name='pig')                  # 2 keyword arguments

  5. animal('cat', 'smile', 'jump')                      # 3 positional arguments

  6. animal('bird', state='inactive')                    # 1 positional, 1 keyword

以下都是无效的调用方式。

  1. animal()                       # required argument missing

  2. animal(name='rabbit', 'dead')  # non-keyword argument after a keyword argument

  3. animal('fox', name='bird')     # duplicate value for the same argument

  4. animal(profile='warning')      # unknown keyword argument

函数调用时,关键字参数必须跟在位置参数后面。所有传递的关键字参数都必须匹配一个函数接受的参数。

例如,profile 不是函数 animal 的有效参数,关键字参数的顺序并不重要。这也包括必选参数

PS: 不能对同一个参数多次赋值。

下面的举例就是一个反例

  1. def get_name(name):

  2.     print(name)

  3. get_name('dog', name='cat')

  4. Traceback (most recent call last):

  5.   File "<stdin>", line 1, in <module>

  6. TypeError: get_name() got multiple values for argument 'name'

在 Python 中形参还可以是 **name 形式,指接收一个字典(kwarg=value),该字典包含与函数中已定义形参对应之外的所有关键字参数。

**name 形参可以与 *name(指接收一个元组,该元组包含形参列表之外的位置参数)形参组合使用,请注意:*name 必须在 **name 前面

示例代码

Input:

  1. def animalShop(animal_name, *arguments, **keywords):

  2.     print("-- Do you have any", animal_name, "?")

  3.     print("-- I'm sorry, we're all out of", animal_name)

  4.     for argument in arguments:

  5.         print(argument)

  6.     print("*" * 40)

  7.     for keyword in keywords:

  8.         print(keyword, ":", keywords[keyword])

animalShop函数可以使用如下方式调用

  1. animalShop("dog", "It's very Cute, sir.",

  2.            "It's really very, VERY Smart, sir.",

  3.            shopkeeper="tony",

  4.            client="Online Shop",

  5.            market="Chat Connect")

Output:

  1. -- Do you have any dog ?

  2. -- I'm sorry, we're all out of dog

  3. It's very Cute, sir.

  4. It's really very, VERY Smart, sir.

  5. ****************************************

  6. shopkeeper : tony

  7. client : Online Shop

  8. market : Chat Connect

PS: 注意,关键字参数在输出结果中的顺序与调用函数时的顺序一致。

三、特殊参数

在 Python 函数中,默认情况下,参数可以按位置显式关键字传递。

但为了让代码易读、高效,最好限制参数的传递方式,这样,开发者只需查看函数定义,即可确定参数项是仅按位置按位置或关键字,还是仅按关键字传递。

函数定义如下:

  1. def function(position_arg1, position_arg2, /, position_or_keyword, *, kwd1, kwd2):

  2.    pass

function函数定义中:

  • position_arg1position_arg2仅仅只是位置参数

  • position_or_keyword可以是位置参数也可以是关键字参数

  • kwd1kwd2仅仅只是关键字参数

PS: /*是可选的,这些符号表明形参如何把参数值传递给函数:位置、位置或关键字、关键字。关键字形参也叫作命名形参。

3.1 位置或关键字参数

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

3.2 仅位置参数

细节补充一下,特定形参可以标记为 仅限位置

  • 仅限位置时,形参的顺序很重要,且这些形参不能用关键字传递。仅限位置形参应放在 / (正斜杠)前。

  • / 用于在逻辑上分割仅限位置形参与其它形参。如果函数定义中没有 /,则表示没有仅限位置形参。

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

3.3 仅限关键字参数

细节补充一下,把形参标记为 仅限关键字,表明必须以关键字参数形式传递该形参,应在参数列表中第一个 仅限关键字 形参前添加 *

示例代码

如下函数定义示例,注意 / 和 * 标记。

  1. def standard_arg(arg):

  2.     print(arg)

  3. def pos_only_arg(arg, /):

  4.     print(arg)

  5. def kwd_only_arg(*, arg):

  6.     print(arg)

  7. def union_example(pos_only, /, standard, *, kwd_only):

  8.     print(pos_only, standard, kwd_only)

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

  1. # 按位置传递参数

  2. standard_arg(200)

  3. 200

  4. # 按关键字传递参数

  5. standard_arg(arg=200)

  6. 200

第二个函数 pos_only_arg 的函数定义中有 /,仅限使用位置形参

  1. # 按位置传递参数正常,无报错

  2. pos_only_arg(100)

  3. 100

  4. # 按关键字传递参数异常,会抛异常

  5. pos_only_arg(arg=100)

  6. Traceback (most recent call last):

  7.   File "<stdin>", line 1, in <module>

  8. TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

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

  1. # 按位置传递参数异常,会抛异常

  2. kwd_only_arg(300)

  3. Traceback (most recent call last):

  4.   File "<stdin>", line 1, in <module>

  5. TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

  6. # 按关键字传递参数正常,无报错

  7. kwd_only_arg(arg=300)

  8. 3

最后一个函数 union_example 在同一个函数定义中,使用了全部三种调用方式

  1. # 第3个参数仅为关键字参数,这里使用位置参数传参,会报错

  2. union_example(100, 200, 300)

  3. Traceback (most recent call last):

  4.   File "<stdin>", line 1, in <module>

  5. TypeError: union_example() takes 2 positional arguments but 3 were given

  6. # 第2个参数使用位置参数传参,无报错

  7. union_example(100, 200, kwd_only=300)

  8. 输出:100 200 300

  9. # 第2个参数使用关键字参数传参,无报错

  10. union_example(100, standard=200, kwd_only=300)

  11. 输出:100 200 300

  12. # 第1个参数仅为位置参数,这里使用关键字参数传参,会报错

  13. union_example(pos_only=100, standard=200, kwd_only=300)

  14. Traceback (most recent call last):

  15.   File "<stdin>", line 1, in <module>

  16. TypeError: union_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

位置参数与关键字参数key名称冲突

在 key_error 函数定义中,kwargs 把 name 当作键,可能与位置参数 name 产生潜在冲突

Input:

  1. def key_error(name, **kwargs):

  2.     return 'name' in kwargs

Output:

  1. key_error('tony', **{'name': 'lucy'})

  2. Traceback (most recent call last):

  3.   File "<stdin>", line 1, in <module>

  4. TypeError: key_error() got multiple values for argument 'name'

冲突解决方法

在位置参数 name 后加上 / (仅限位置参数)就可以了。

此时,函数定义把 name 当作位置参数,'name' 也可以作为关键字参数的键了。

即:仅限位置形参的名称可以在 **kwargs 中使用,不会产生冲突。

  1. def key_error(name, **kwargs):

  2.     return 'name' in kwargs

  3. key_error('tony', **{'name': 'lucy'})

  4. True

四、任意实参列表

在 Python 函数调用过程中使用任意数量的实参是不常见的,通常这些实参包含在元组中。

在可变数量的实参之前,可能有若干个普通参数,如下示例代码。

  1. def person(p_file, separator, *args):

  2.     p_file.write(separator.join(args))

*args 形参后的任何形式参数只能是仅限关键字参数,即只能用作关键字参数,不能用作位置参数,如下示例代码。

  1. def test_concat(*args, sep="/"):

  2.     return sep.join(args)

  3. test_concat("name", "sex", "age")

  4. 'name/sex/age'

  5. test_concat("name", "sex", "age", sep=".")

  6. 'name.sex.age'

五、解包实参列表

在 Python 中函数调用需要独立的位置参数,如果是可变数量的位置参数,它的实参在列表或元组里时,要执行相反的操作。

例如,常用的内置 range() 函数要求传递独立的 start 和 stop 实参。

如果这些参数不是独立的,则要在调用函数时,用 * 操作符把实参从列表或元组解包出来。

示例代码

  1. list(range(1, 4))            # normal call with separate arguments

  2. 输出:[1, 2, 3]

  3. moreNumber_args = [1, 4]

  4. list(range(*moreNumber_args)) # call with arguments unpacked from a list

  5. 输出:[1, 2, 3]

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

  1. def animal(name, state='active', action='fly'):

  2.     print("This animal wouldn't", action, end=' ')

  3.     print("if you reminder", name, "you are great.", end=' ')

  4.     print("-- They're", state, "!")

  5.  

  6. d = {"name": "dog", "state": "active", "action": "run"}

  7. animal(**d)

  8. 输出:This animal wouldn't run if you reminder dog you are great. -- They're active !

行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 320231853,里面有各种软件测试+开发资料和技术可以一起交流学习哦。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值