Loading... ## 1. 函数参数的初识 ``` 在我们之前的函数学习中, 写的函数的代码都是写死的. 我们知道, 函数是以功能为导向的, 写死的话函数里面更改起来会很麻烦. 试想一下, 我们在使用陌陌等软件的时候, 可不可以进行筛选, 比如选择性别、年龄等? ``` ``` 再比如我们之前学过的 ``` `len()`, 这个 `len()` 它是可以获取某个字符串的个数, 也可以获取某个列表的个数. 但是, 你更改 `len()`里面代码了吗? 并没有. ```python s1 = 'qwer' li = [1, 3, 5] print(len(s1)) print(len(li)) ``` ``` 其实我们写的函数也可以将一些数据传到函数里面, 然后让里面的代码利用上这个数据. ``` 举个例子: ```python def momo(sex): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s' % (sex)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('女') ``` 上面就是函数传参的示例, 函数的参数可以从两个角度进行划分: - 形参 - 写在 **函数定义** 时的位置的变量叫形参. 表示这个函数需要xxx - 实参 - 在 **函数调用** 的时候给函数传的值, 叫做实参. 这个是实际执行的时候给函数传的值.表示给函数xxx 函数的传参就是函数**将实际参数交给形式参数**的过程. ```python def momo(sex): # 函数定义时的参数, 这个就是形参 print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s' % (sex)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('女') # 函数执行时的参数, 这个就是实参 ``` 这个过程就是: 代码运行到 `momo('女')`, 开始执行此函数, 并且将字符串 `'女'`这个数据传给变量 `sex`, 然后执行函数中的代码. 如果遇到 `sex`, 其实就是 `'女'`这个数据. ``` 我们刚才说到, 函数的参数分为实参和形参两种. 我们先从实参的角度进行讲解. ``` ## 2.函数的实参 ### 2.1 位置参数 ``` ``` **位置参数就是从左至右, 实参与形参一一对应**. ```python def momo(sex, age, hobby): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s, 年龄: %s, 爱好: %s' % (sex, age, hobby)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('女', '23-25', '唱歌') ``` **练习** 编写函数, 给函数传两个参数x和y, x, y相加, 返回相加的和 ```python def func(x, y): z = x + y return z num_sum = func(1, 2) print(num_sum) # 结果: 3 ``` 编写函数, 给函数传两个参数x和y, 比较x, y的大小, 返回大的那个数 ```python def func(x, y): if x > y: return x else: return y num = func(1, 2) print(num) # 结果: 2 ``` ``` 比较大小的这个写法比较麻烦, 在这里我们学一下三元运算符 ``` ```python def func(x, y): z = x if x > y else y # 当x大于y, 就把x赋值给z, 当x小于y, 就把y赋值给z return z num = func(1, 2) print(num) # 结果: 2 ``` ### 2.2 关键字参数 ``` 当函数的参数比较少时, 位置参数还很好用, 但是如果参数很多, 我们不仅需要记住这个函数有哪些参数, 还需要记住每个参数的顺序, 否则函数就不能正常调用了, 这该怎么办呢? Python提出了一种 ``` `关键字参数`, 我们不需要记住每个参数的位置, 只需要知道每个参数的名字就可以了. ```python def momo(sex, age, hobby): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s, 年龄: %s, 爱好: %s' % (sex, age, hobby)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo(sex='女', hobby='唱歌', age='23-25') ``` 这样子, 我们就不需要记住每个参数的位置了. ### 2.3 混合参数 ``` 学了位置参数与关键字参数之后, 我们也可以把上面两种参数混合使用. 也就是说在调用函数的时候即可以给出 ``` `位置参数`, 也可以给出 `关键字参数`. 有一点需要注意的是: **位置参数一定要在关键字参数的前面.** ```python def momo(sex, age, hobby): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s, 年龄: %s, 爱好: %s' % (sex, age, hobby)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('女', hobby='唱歌', age='23-25') ``` 简单总结一下: 从**实参的角度**来看, 参数分为三种: - 位置参数 - 关键参数 - 混合参数 - **位置参数必须要在关键字参数前面** 下面我们再从形参角度进行分析. ## 3.函数的形参 ### 3.1 位置参数 ``` 在函数的形参中, 其位置参数与函数实参中的位置参数是一样的, 都是按照位置从左至右, 一一对应 ``` ```python def momo(sex, age, hobby): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s, 年龄: %s, 爱好: %s' % (sex, age, hobby)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('女', '23-25', '唱歌') ``` ### 3.2 默认值参数 ``` 在函数定义的时候, 我们就可以给出函数参数的值, 这个值我们称之为 ``` `默认值`. 默认值参数一般是这个参数的使用率较高, 才会设置默认值参数. ``` 比如我们之前学过的 ``` `open()`函数, 通过查看源码就可以知道, 里面的 `mode=r`就是默认值参数. 那么对于我们写的这个交友功能的函数来说, 我们一般希望我们找到的都是女性, 那么我们就可以把 `sex`设置成默认值参数. ```python def momo(age, hobby, sex='女'): print('拿出手机') print('打开陌陌') print('设置筛选条件: 性别: %s, 年龄: %s, 爱好: %s' % (sex, age, hobby)) print('左滑一下') print('右滑一下') print("看见个漂亮的妹子") print("问她,约不约啊!") print("ok 走起") momo('23-25', '唱歌') ``` 同样需要注意的是: **位置参数要在默认值参数的前面** 如果实参不传参, 则形参使用默认参数. ### 3.3 动态参数 我们在学习了位置参数与默认值参数之后, 会很容易的发现一个麻烦的地方. 就是如果我有很多的实参, 那么我就必须写等数量的形参去对应的接收, 如果不这样, 就会报错. ```python def eat(a, b, c): print('我请您吃: %s, %s, %s' % (a, b, c)) eat('蒸羊羔', '蒸熊掌', '蒸鹿尾儿', '烧花鸭') # 报错 ``` 假如我们在传参的时候不是很清楚有哪些参数, 或者我们给函数传了很多实参, 就要对应写很多形参, 这样很麻烦, 怎么办? 这个时候我们就要考虑马上要学习的动态参数(万能参数)了. 动态参数分为两种: - 动态接收**位置参数**的*args - 动态接收**关键字参数**的\*\*kwargs #### 3.3.1 动态接收位置参数的*args ``` 我们按照上面的例子继续写, 如果我请您吃的东西很多, 但是我们不想用多个参数接收, 那么我就可以使用动态参数 ``` `*args` ```python def eat(*args): print('我请您吃: ', args) eat('蒸羊羔', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅') # 结果 # 我请您吃: ('蒸羊羔', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅') ``` 首先我们来说一下 `args`, `args`就是一个普通的形参, 但是如果你在 `args`的前面加一个 `*`, 那么它就有了特殊的意义. 在python中, 我们知道 `*`是表示乘法运算, 那么在**函数的定义**中, `*`相当于一种聚合的功能, `*args`就会把实参中所有的**位置参数**接收, 聚合到一个**元组**中, 并将这个元组赋值给**args**. **练习** 传入函数中数量不定的int型数据, 函数计算所有数的和并返回. ```python def func(*args): n = 0 for i in args: n += i return n ``` #### 3.3.2 动态接收关键字参数的\*\*kwargs ``` 我们知道了 ``` `*args`可以把实参中所有的位置参数聚合到一个元组中. 同样的, `**kwargs`是把实参中所有的**关键字参数**聚合到一个字典中. 其中 `kwargs`这个名字也是我们约定俗成的. ``` 也就是说, ``` `**kwargs` 是接收所有的**关键字参数**, 然后将其转换成一个**字典**赋值给**kwargs**这个形参. ```python def func(**kwargs): print(kwargs) # {'name': 'Conan', 'age': 18} func(name='Conan', age=18) ``` 紧接着, 我们来看一下 `*args`与 `**kwargs`一起使用的写法: ```python def func(*args, **kwargs): print(args) # ('蒸羊羔', '蒸熊掌', '蒸鹿尾儿') print(kwargs) # {'name': 'Conan', 'age': 18} func('蒸羊羔', '蒸熊掌', '蒸鹿尾儿', name='Conan', age=18) ``` 如果函数在定义时设置了动态参数, 那么它可以接收所有的位置参数与关键字参数, 这样就会大大提升函数的扩展性, 针对实参参数较多的情况下, 解决了一一对应的麻烦. > 最后, 在强调一下, 动态参数并不是必须写成 `*args`与 `**kwargs`, 只有前面的 `*`星号才是必须的. > > 你完全可以写成 `*a`和 `**a`, 但是我们一般约定俗成才写成 `*args`与 `**kwargs`. ### 3.4 仅限关键字参数 ``` ``` **仅限关键字参数**是python3的新特性, 它的位置要放在 `*args`的后面, `**kwargs`的前面. 它与默认参数的前后顺序无所谓, 它只接收**关键字**传的参数. ```python # 这样传参是错误的,因为仅限关键字参数c只接受关键字参数 def func(a, b, *args, c): print(a, b) print(args) func(1, 2, 3, 4, 5) # 这样就正确了: def func(a, b, *args, c): print(a, b) # 1 2 print(args) # (3, 4) print(c) # 5 func(1, 2, 3, 4, c=5) ``` ### 3.5 形参的顺序 ``` 到目前为止, 从形参角度我们也把各种类型都讲完了, 现在我们来看一下它们的排列顺序是怎样的. ``` ``` 首先, 位置参数与默认参数之前已经确定了, ``` **位置参数必须在默认参数前面**. 即: 位置参数, 默认参数. ``` 那么动态参数 ``` `*args`与 `**kwargs`放在那里呢? ``` ``` `*args`肯定不能放在位置参数的前面, 这样的话位置参数就接收不到具体的实参了: ```python # 这样位置参数a,b始终接收不到实参了,因为args全部接受完了 def func(*args, a, b, sex='男'): print(args) print(a, b) func(1, 2, 3, 4, 5) # 报错 ``` 那么动态参数必须在位置参数后面, 它可以在默认参数后面吗? ```python # 这样也不行,我的实参的第三个参数始终都会将sex覆盖掉,这样失去了默认参数的意义。 def func(a, b, sex='男', *args, ): print(a, b) # 1 2 print(args) # (4, 5) print(sex) # 3 func(1, 2, 3, 4, 5) ``` 虽然这样写不会报错, 但是这样就会使默认参数失去意义. 所以 `*args`一定要在位置参数与默认值参数的中间: 位置参数, *args, 默认参数. 那么 `**kwargs`应该放在哪里呢? `**kwargs`可以放在默认参数前面吗? ```python # 直接报错:因为**kwargs是接受所有的关键字参数,如果你想改变默认参数sex,你永远也改变不了,因为它会先被**kwargs接受。 def func(a, b, *args, **kwargs, sex='男'): print(a, b) print(args) print(kwargs) print(sex) func(1, 2, 3, 4, 5) ``` 所以现在, 形参的顺序为: 位置参数, *args, 默认参数, \*\*kwargs. 那么对于仅限关键字参数, 我们在上面已经说了. 故最终的形参角度中, 所有的形参的顺序为: **位置参数, *args, 默认参数, 仅限关键字参数, \*\*kwargs.** 最后, 做一道题巩固一下吧: ```python def foo(a, b, *args, c, sex=None, **kwargs): print(a, b) print(args) print(c) print(sex) print(kwargs) # foo(1,2,3,4,c=6) # foo(1,2,sex='男',name='Conan',hobby='code') # foo(1,2,3,4,name='alex',sex='男') # foo(1,2,c=18) # foo(2, 3, [1, 2, 3],c=13,hobby='喝茶') ``` Last modification:July 23rd, 2020 at 12:13 pm © 允许规范转载 Support ×Close Appreciate the author Sweeping payments Pay by AliPay Pay by WeChat