详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)

Python函数参数传参的种类

  Python中函数参数定义及调用函数时传参大体可分必备参数、关键字参数、默认可省略参数、不定长元组参数、不定长关键字参数等,下面通过函数定义及调用上的不同情况深入解析这些中参数的不同及应用场景。

  为了更好的理解不同参数的具体意义,所以下面演示代码中,使用的参数数量较多。具体是一个调用MySQL数据库配置参数的函数所需要的参数,我们用这个来演示不同类型的特点及适用方法,了解每种类型的应用场景及优缺点。

必备参数__仅赋值传参

  必备参数,就是在调用函数的时候,定义的参数要全部都有赋值,否则执行的时候代码会报错。

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用必备参数
  4. def demo_get_conf1(user, pw, host, port, db, charset):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', user)
  9. print('pw: ', pw)
  10. print('db: ', db)
  11. print('charset: ', charset)
  12. demo_get_conf1('root', '1234', '127.0.0.1', '3306', 'tests', 'utf8')

  上述代码中,调用demo_get_conf1函数的时候,定义的所有参数都必须传递,并且要按照规定的顺序传递,否则函数体内得到的也是错误的。以上代码控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: root
  4. pw: 1234
  5. db: tests
  6. charset: utf8

  这也是我们希望得到的正确结果。

  下面我们把上述代码最后一行调用的前两个参数("root"和"1234")对调一下:

  1. demo_get_conf1('1234', 'root', '127.0.0.1', '3306', 'tests', 'utf8')

  执行后控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: 1234
  4. pw: root
  5. db: tests
  6. charset: utf8

  很显然,得到的 user 变成了 1234,pw 变成了 root,也就是说是完全按照位置来对应函数定义时的参数变量,所以传参的时候,顺序不能错,参数比较多的时候,就不容易记住顺序了。那么Python还给大家一种传递方式,同样是上面的函数,可以不用按顺序传参,请看下节“必备参数__键值对传参”。

必备参数__键值对传参(关键字参数)

  同样是必备参数,但是在代用函数传参的时候,可以直接使用键值对的方式,看下面演示代码:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用必备参数
  4. def demo_get_conf1(user, pw, host, port, db, charset):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', user)
  9. print('pw: ', pw)
  10. print('db: ', db)
  11. print('charset: ', charset)
  12. demo_get_conf1(
  13. charset='utf8',
  14. pw='1234',
  15. user='root',
  16. host='127.0.0.1',
  17. port='3306',
  18. db='tests')

  控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: root
  4. pw: 1234
  5. db: tests
  6. charset: utf8

  函数定义还是与上例一样,只是调用函数的时候,参数传递使用了键值对,键名就是参数定义时的变量名,这样就可以不用理会顺序,只要记住键名(参数变量名)就可以了。

  但是这毕竟是必备参数,所有的参数都要传递,否则会报错,例如将调用修改成:

  1. demo_get_conf1(
  2. pw='1234',
  3. user='root',
  4. host='127.0.0.1',
  5. port='3306',
  6. db='tests')

  控制台会输出:

  1. TypeError: demo_get_conf1() missing 1 required positional argument: 'charset'

  提示缺少'charset'参数,代码不能正常运行。那么有没有可以缺省参数,用就传递,不用就不传递的,Python肯定也有这种参数,继续看下一节“默认可省略参数”。

默认可省略参数

  默认可省略参数,就是在定义的时候就给了默认值,如果在函数调用的时候给这个参数传值了,那么就使用传递的值,如果没有传递就使用定义时候的默认值。

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用默认可省略参数
  4. def demo_get_conf2(user, pw, host, port, db, charset='utf8'):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', user)
  9. print('pw: ', pw)
  10. print('db: ', db)
  11. print('charset: ', charset)
  12. demo_get_conf2('root', '1234', '127.0.0.1', '3306', 'tests')

  在上述代码中,调用demo_get_conf2函数的时候,我们并没有传递第六个参数charset,但是代码没有报错,控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: root
  4. pw: 1234
  5. db: tests
  6. charset: utf8

  可见参数"charset"虽然在调用的时候没有传递,但是依然得到了值"utf8",这个值就是在函数定义时候,参数"charset"赋值的默认值。

  但是这个参数必须在后面,否则调用的时候按照顺序赋值的时候,少传递一个就不是这个有默认值的了,所以Python不允许那样做,看下面的例子:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用默认可省略参数
  4. def demo_get_conf1(user, pw, host, port, db='tests', charset):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', user)
  9. print('pw: ', pw)
  10. print('db: ', db)
  11. print('charset: ', charset)
  12. demo_get_conf1('root', '1234', '127.0.0.1', '3306', 'tests')

  控制台输出:

  1. SyntaxError: non-default argument follows default argument

  提示非默认参数不能在默认参数之后,代码不能正常执行。

  有些时候,我们不能确定要具体传递几个参数,应用场景需要的参数数量差异较大,那么是否可以动态传递不同数量的参数呢,继续看下一节“可变长元组参数”

不定长元组参数(*args)

  不定长元组参数,就是不确定数量的参数,定义一个参数把传入的参数组合成元组,来接收函数调用时传递过来的N个参数,在函数体内以元组形式按顺序读取。为了演示更多是使用场景,下面没有使用网络中通常使用的循环方式来取可变长元组参数。

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用可变长元组参数
  4. def demo_get_conf3(host, port, *cnf):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', cnf[0])
  9. print('pw: ', cnf[1])
  10. print('db: ', cnf[2])
  11. print('charset: ', cnf[3])
  12. demo_get_conf3('127.0.0.1', '3306', 'root', '1234', 'tests', 'utf8')

控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: root
  4. pw: 1234
  5. db: tests
  6. charset: utf8

  可见这是我们正常需要得到的结果,可以多传更多的参数,只要里面取值的数量没有超过传递过来可变参数的数量,就不会报错。

  在上例中,host 和 port 是必备参数,函数调用的时候,在这两个参数后面所传递的,就都是对应到函数定义时的变长参数元组里了。

  在Python中,函数参数是可以使用元组的,那么这总定义与直接使用元组参数有什么区别呢,下面看使用元组参数的示例:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用元组参数
  4. def demo_get_conf4(host, port, cnf):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', cnf[0])
  9. print('pw: ', cnf[1])
  10. print('db: ', cnf[2])
  11. print('charset: ', cnf[3])
  12. demo_get_conf4('127.0.0.1', '3306', ('root', '1234', 'tests', 'utf8'))

  控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: root
  4. pw: 1234
  5. db: tests
  6. charset: utf8

  与上一例比,函数体一模一样,参数 cnf 只是少了个 “*”。重点是调用的时候不同,可以看出明显的区别,可变长元组参数在调用的时候,可以与必备参数一样依次传递,而定义元组类型参数,调用函数传递参数时,需要传递元组类型的数据才可以。

  这种参数传递的时候,元组里面的元素也是要强调顺序的,如果是累加一类的函数,顺序不重要,如果是每个元素都代表不同具体含义的,那顺序就十分重要,不可以搞错,否则与必备参数一样,会在函数体内取值错误。

  在上两例中,只能算是元组参数,还不能算不定长,因为函数体内的取值规定了元组的元素数量,那接下来看一个网络上通常写法的例子:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用可变长元组参数
  4. def demo_get_conf5(x, y, *nums):
  5. "得到累加和"
  6. res = x + y
  7. for i in nums:
  8. res += i
  9. return res
  10. print(demo_get_conf5(10, 20,))
  11. print(demo_get_conf5(10, 20, 15, 25, 30))
  12. print(demo_get_conf5(10, 20, 15, 25, 30, 50, 30))

  控制台输出:

  1. 30
  2. 100
  3. 180

  上例中,前两个参数是必须传的,后面的参数可传可不传,传的数量也不固定,根据需要由外部调用决定,所以这是可变长参数。但是这种应用适合元组内参数是相同类型和作用,如果回到上面的配置参数应用中,是否可以不定长的呢,看下面的代码:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用可变长元组参数
  4. def demo_get_conf6(host, port, *cnf):
  5. "打印得到的数据库配置"
  6. arr = ['root', '1234', 'tests', 'utf8'] # 可变参数的默认值
  7. len_arr = len(arr)
  8. len_cnf = len(cnf)
  9. if len_cnf > len_arr:
  10. len_cnf = len_arr # 取变长参数最多不超过默认列表中的数量,多余的忽略
  11. for i in range(len_cnf):
  12. arr[i] = cnf[i]
  13. print('host: ', host)
  14. print('port: ', port)
  15. print('user: ', arr[0])
  16. print('pw: ', arr[1])
  17. print('db: ', arr[2])
  18. print('charset: ', arr[3])
  19. print('\n', '传递5个变长参数')
  20. demo_get_conf6('127.0.0.1', '3306', 'new_user', '5678', 'tests', 'utf8mb4', 'abc')
  21. print('\n', '传递4个变长参数')
  22. demo_get_conf6('127.0.0.1', '3306', 'new_user', '5678', 'tests', 'utf8mb4')
  23. print('\n', '传递2个变长参数')
  24. demo_get_conf6('127.0.0.1', '3306', 'new_user', '5678')
  25. print('\n', '不传递变长参数')
  26. demo_get_conf6('127.0.0.1', '3306')

  控制台输出:

  1. 传递5个变长参数
  2. host: 127.0.0.1
  3. port: 3306
  4. user: new_user
  5. pw: 5678
  6. db: tests
  7. charset: utf8mb4
  8. 传递4个变长参数
  9. host: 127.0.0.1
  10. port: 3306
  11. user: new_user
  12. pw: 5678
  13. db: tests
  14. charset: utf8mb4
  15. 传递2个变长参数
  16. host: 127.0.0.1
  17. port: 3306
  18. user: new_user
  19. pw: 5678
  20. db: tests
  21. charset: utf8
  22. 不传递变长参数
  23. host: 127.0.0.1
  24. port: 3306
  25. user: root
  26. pw: 1234
  27. db: tests
  28. charset: utf8

  可以看出,传递5个变长参数的,多出那个“abc”参数被忽略掉了,余下的四个参数都按照传递的值取到了;传递4个变长参数的,完全吻合,得到的都是传递的参数;传递2个变长参数的,前两个变长参数是调用传输时传递的值,后两个则是使用的默认值;不传递变长参数的,变长参数全部使用了默认值。这就实现了不同用途的变长参数取值并都可以设置默认值的目的,在一定范围内实现了不定长。

  但是这种变长参数,都还是要保证传递顺序的,元组里的顺序如果传递错误,对于后面例子那获取的数据就是错误的。是否可以不定长有不用理会顺序呢,继续看下一节“不定长字典参数”。

不定长字典参数(**kwargs)

  不定长字典参数,就是不确定数量的参数,定义一个字典,按键值对形式来接收函数调用时传递过来的N个参数,在函数体内以字典形式按键值对读取。这样在传递的时候,就可以不用在意顺序问题了,看下面的代码:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用可变长字典参数
  4. def demo_get_conf7(host, port, **cnf):
  5. "打印得到的数据库配置"
  6. print('host: ', host)
  7. print('port: ', port)
  8. print('user: ', cnf['user'])
  9. print('pw: ', cnf['pw'])
  10. print('db: ', cnf['db'])
  11. print('charset: ', cnf['charset'])
  12. demo_get_conf7('127.0.0.1', '3306', user='new_user', pw='5678', db='tests', charset='utf8mb4')

  控制台输出:

  1. host: 127.0.0.1
  2. port: 3306
  3. user: new_user
  4. pw: 5678
  5. db: tests
  6. charset: utf8mb4

  在上述代码中,可以看出,在函数体内实际把**cnf参数当做字典来读取,那么与把函数参数直接定义成字典来用相比较,对函数体内是没有区别的,但是在函数调用的时候,参数传递就有差别了。如果参数定义成字典,那么调用的时候就需要传递字典,否则会报错,如下面代码:

  1. # 演示获得数据库配置参数,使用字典参数
  2. def demo_get_conf7(host, port, cnf):
  3. "打印得到的数据库配置"
  4. print('host: ', host)
  5. print('port: ', port)
  6. print('user: ', cnf['user'])
  7. print('pw: ', cnf['pw'])
  8. print('db: ', cnf['db'])
  9. print('charset: ', cnf['charset'])
  10. demo_get_conf7('127.0.0.1', '3306', {'user':'new_user', 'pw':'5678', 'db':'tests', 'charset':'utf8mb4'})

  与上一例比,函数体一模一样,参数 cnf 只是少了两个 “*”。重点是调用的时候不同,可以看出明显的区别,可变长字典参数在调用的时候,可以直接写键名,不用引号,使用“=”赋值,而定义字典类型参数,调用函数传递参数时,需要传递字典类型的数据才可以。

  在上线的例子中,函数体内的代码变相等于指定了不定长参数**cnf的长度,但参数是可以变长的是确定的,主要是函数体里面取值的代码逻辑。如果循环打印输出,就可以任意变长,但是实际项目中这样做适用场景不多。还是取数据库配置这个需求,我们改写一下函数体的代码:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 演示获得数据库配置参数,使用字典参数
  4. def demo_get_conf7(host, port, **cnf):
  5. "打印得到的数据库配置"
  6. arr = {'user': 'root', 'pw': '1234', 'db': 'tests', 'charset': 'utf8'} # 可变参数的默认值
  7. for key, val in cnf.items():
  8. if key in arr:
  9. arr[key] = val # 取变长参数传递过来的键,如果在预置里面存在就更改,不存在的忽略
  10. print('host: ', host)
  11. print('port: ', port)
  12. print('user: ', arr['user'])
  13. print('pw: ', arr['pw'])
  14. print('db: ', arr['db'])
  15. print('charset: ', arr['charset'])
  16. print('----------参数完整传递:')
  17. demo_get_conf7('127.0.0.1', '3306', user='new_user', pw='5678', db='tests', charset='utf8mb4')
  18. print('----------参数多余传递:')
  19. demo_get_conf7('127.0.0.1', '3306', user='new_user', pw='5678', db='tests', charset='utf8mb4', abc='123')
  20. print('----------参数减少传递:')
  21. demo_get_conf7('127.0.0.1', '3306', user='new_user', pw='5678')
  22. print('----------变参没有传递:')
  23. demo_get_conf7('127.0.0.1', '3306')

  控制台输出:

  1. ----------参数完整传递:
  2. host: 127.0.0.1
  3. port: 3306
  4. user: new_user
  5. pw: 5678
  6. db: tests
  7. charset: utf8mb4
  8. ----------参数多余传递:
  9. host: 127.0.0.1
  10. port: 3306
  11. user: new_user
  12. pw: 5678
  13. db: tests
  14. charset: utf8mb4
  15. ----------参数减少传递:
  16. host: 127.0.0.1
  17. port: 3306
  18. user: new_user
  19. pw: 5678
  20. db: tests
  21. charset: utf8
  22. ----------变参没有传递:
  23. host: 127.0.0.1
  24. port: 3306
  25. user: root
  26. pw: 1234
  27. db: tests
  28. charset: utf8

  从控制台结果可以看出,多余传递的可变长参数被忽略掉了,少传的可变长参数使用了函数体内的默认值。可变长参数如果一个也没传递,那就完全使用了函数体内的默认值。

  以上是对Python中函数的不同类型参数的区别及应用场景例举,一点心得体会,希望对有兴趣的朋友能有所帮助!

详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)的更多相关文章

  1. python 函数的定义及传参

    函数是一个通用的程序结构部件,是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 定义一个简单的函数: >>> def test(a): #创建一个函数,函数名是test ...

  2. 详解python函数的参数

    详解python函数的参数 一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号, 隔开 案列: ...

  3. 深入理解PHP内核(十二)函数-函数的定义、传参及返回值

    原文链接:http://www.orlion.ga/344/ 一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) {    echo $var; ...

  4. 深入理解PHP内核(六)函数的定义、传参及返回值

    一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) { echo $var; } 1.词法分析 在Zend/zend_language_scann ...

  5. react router @4 和 vue路由 详解(六)vue怎么通过路由传参?

    完整版:https://www.cnblogs.com/yangyangxxb/p/10066650.html 8.vue怎么通过路由传参? a.通配符传参数 //在定义路由的时候 { path: ' ...

  6. react router @4 和 vue路由 详解(五)react怎么通过路由传参

    完整版:https://www.cnblogs.com/yangyangxxb/p/10066650.html 7.react怎么通过路由传参? a.通配符传参(刷新页面数据不丢失) //在定义路由的 ...

  7. c语言-函数的定义及传参

    模块化编程 定义: 数据类型 函数名(型参):如果没有数据类型,默认的为int 值传递 地址传递 嵌套调用 递归调用:直接或间接调用本身函数,求可能栈破裂,求阶乘 #include <stdio ...

  8. python不定长参数 *argc,**kargcs(19)

    在 python函数的声明和调用 中我们简单的了解了函数的相关使用,然而在函数传递参数的时候,我们埋下了一个坑,关于不定长参数的传递我们还没有讲,今天这篇文章主要就是讲解这个问题. 一.函数不定长参数 ...

  9. 举例详解Python中的split()函数的使用方法

    这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下   函数:sp ...

随机推荐

  1. Object.defineProperty和Object.freeze、Object.seal

    目录 一 Object.defineProperty 1.1 用法 1.2 数据描述 1.2.1 value 1.2.2 writable 1.2.3 enumerable 1.2.4 configu ...

  2. go基础之不定参函数

    指定类型参数 任意类型参数的变参 go语言同其他编程一样也提供了对变参函数的支持.本文简单讲解一下go中变参函数的使用方法. 指定类型参数 不定参数是指函数传入参数的个数为不确定数量,个数需要在调用的 ...

  3. Spring Boot Validation,既有注解不满足,我是怎么暴力扩展validation注解的

    前言 昨天,我开发的代码,又收获了一个bug,说是界面上列表查询时,正常情况下,可以根据某个关键字keyword模糊查询,后台会去数据库 %keyword%查询(非互联网项目,没有使用es,只能这样了 ...

  4. 2019CCPC秦皇岛 J MUV LUV EXTRA(KMP)

    MUV LUV EXTRA Time Limit: 2000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)T ...

  5. BZOJ3110[Zjoi2013]K大数查询(树状数组+整体二分)

    3110 [Zjoi2013]K大数查询 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a ...

  6. SpringBoot系列之集成Mybatis教程

    SpringBoot系列之集成Mybatis教程 环境准备:IDEA + maven 本博客通过例子的方式,介绍Springboot集成Mybatis的两种方法,一种是通过注解实现,一种是通过xml的 ...

  7. tensorflow常用函数解释

    从二维数组中选一个矩形 import tensorflow as tf data = [[1,2,3,4,5,6,7,8],[11,12,13,14,15,16,17,18]] x = tf.stri ...

  8. spring源码学习之设计模式(1)单例模式

    众所周知,单例模式分为饿汉式和懒汉式,昨天在看了<spring5核心原理与30个类手写实战>之后才知道饿汉式有很多种写法,分别适用于不同场景,避免反射,线程不安全问题.下面就各种场景.采用 ...

  9. 小白学 Python 爬虫(20):Xpath 进阶

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  10. 201871010119-帖佼佼《面向对象程序设计(java)》第十四周学习总结

    博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...