题主会有这样的疑问是因为对Python函数的参数还了解不多。位置参数也不一定是必传的,事实上在下面你会看到参数是否必传(也就是有无默认值)与它是位置的还是关键字的根本无关。下文黄底图片的例子能充分为你展示出这套规则的灵活性。

————分割线:Python参数体系的完整介绍————

千字预警!接下来的内容试图以最准确、同时尽可能清晰的语言,将Python函数定义时的形式参数,以及函数调用(使用)时的实际参数,其诸多形式分别介绍清楚,乍一看未必好理解,但完全值得反复阅读几次,到时定能给你茅塞顿开之感。

本文假设你了解Python中元组(tuple)和字典(dict)等基本数据类型,并且知道函数定义

# 函数定义,独立成块的代码,圆括号内是形式参数

def funcname(形参表):

函数体

和函数调用的基本形式

# 函数调用,可存在于任意表达式中,圆括号内是实际参数

funcname(实参表)

目录:预备知识(已了解函数基本特征的可跳过)

函数调用

函数定义

函数调用的补充说明

形参默认值

预备知识何为函数?

函数是一段具有特定功能、与主程序隔离开来、易于复用的程序代码。注意“隔离”这个特征,即其内部新定义的变量均应视为其私有的,函数最好只通过参数表和返回值跟外部交换数据。何为函数调用?

函数调用是指

将一组特定数据传递给该函数(传递参数)、

然后启动该函数体的执行、

最后从该函数返回到主程序(相对于被调用的函数而言的,主程序本身也可能是一个函数)中的调用点并带回特定值(返回值)

的过程。

这就给我们提出了两个要求准确地定义该函数,尤其是它的形参表,这规定了我们需要向该函数传递一些什么样的数据;

正确地调用该函数,也就是将我们的实际数据按照正确的形式传递过去,不匹配的行为就会造成错误,可能是解释器报错,也可能是与你意图不符算出错误结果。

请务必清晰区分形式参数(形参)和实际参数(实参),两者的使用规则相互匹配但并不一致,下面将分几节分别介绍,请务必注意区分。

函数调用

为啥要先讲调用?逻辑上函数当然是先定义好才能调用,但事实上,定义的形式是按照调用的需求来的,所以先了解调用形式有利于理解各种定义的缘由。而且调用的形式也比定义的形式要简单和容易理解。

Python函数调用时,实参表由左到右就是简单的两个部分

funcname(【位置实参】,【关键字实参】)

注意这个前后顺序是严格的,两个部分都可以缺省,但不能相互交错!请在阅读下面(尤其是函数定义的部分)时牢牢记住这一点。

位置实参就是由逗号,分隔、按照前后顺序摆放的诸多实际参数,它们按照位置匹配函数定义时的形式参数,以函数func_1为例,

# a, b, c, d就是它的形式参数

def func_1(a, b, c, d):

pass

若按如下方式进行调用

# x, y, z, w就是它的实际参数,可以跟上面的形参重名

func_1(x, y, z, w)

在函数体执行前就会形成

a = x

b = y

c = z

d = w

的传参效果。

关键字实参同样由逗号分隔,区别在于要在实参前加上形参名=这样的前缀,从而实现了一种显式的参数匹配效果,可以摆脱位置的约束。比如还是上面的func_1,我们这次完全使用关键字实参来调用,

func_1(b=y, a=x, d=w, c=z) # 这种形式可以无视位置

当然,单纯的摆脱位置约束还不足以体现关键字参数的优势,结合有意义的形参名和合理设定的默认值,关键字参数可以拥有很好的可读性和易用性。例如pandas库中的一个常用函数read_excel, />形参数目多达二十多个,绝大部分形参名称都有明显含义,且有默认值。这时若要求所有实际参数都按特定位置码放,对调用者和阅读者来说都是很大负担,且不能略过中间已有默认值的参数。这时关键字参数的威力就发挥出来了。比如我可以这样调用它

# 每个参数等号左边是形参,右边是实参

pandas.read_excel(io=my_file_path, index_col=0, sheet_name=1)这就会在函数体执行前形成

io = my_file_path

index_col = 0

sheet_name = 1的传参效果。为了避免逻辑的混乱,我们将在最后讲解默认值的设定,之前我们始终假定形参都没有默认值。

当同时使用两种形式的参数时,就是上面的从左到右两个部分,依然是上面的func_1,这回使用混合调用形式,

func1(x, y, d=w, c=z)

这时你大概可以理解为啥位置实参和关键字实参这两种形式要有严格的前后顺序(尽管关键字实参内部可以无序)了——这确定了位置匹配的唯一性。

必须指出,参数传递允许两种形式,但一个没有默认值的形参必须且只能被匹配一次,不能在位置上匹配一次又在关键字匹配一次。两种实际参数要共同覆盖所有没有默认值的形式参数。

同时,我们也可以清楚地看到一些问题的存在。这种最简形式的func_1定义给了调用者充分的自由,但却缺少了必要的约束,比如没有强制要求前面几个参数必须按位置传递,或者后面几个参数必须按关键字传递。另外,在不设默认值的情况下,我们无法接收变长的实参表(包括变长的位置参数,以及不限名字和数量的关键字参数)。这是函数定义要解决的问题。

函数定义这里重点讨论的是形参表,其他需要注意的主要是函数名funcname与当前模块的所有变量、函数共享命名空间,不要跟已有变量、函数重名就好;函数体的函数代码与主程序是隔离的,拥有独立的命名空间。

函数定义时,所有形参也由逗号,分隔,形式上就是一组普通变量,这些变量用于接收函数调用时传入的具体数据,其作用域为该函数局部,与主程序不冲突。完整地说,它从左到右可以分为五个部分

def funcname(【限定位置形参】,【普通形参】,【特殊形参args】,【限定关键字形参】,【特殊形参kwargs】):

pass

按最简形式定义出来的就是【普通形参】,它们是“位置、关键字兼容”的,也就像上面展示的那样,其他部分是【普通形参】的扩展,满足了函数调用的特殊需求,下面逐一介绍。其中,两个特殊形参分别只能是0个或1个,其他形参可以是0个或多个。这个顺序同样是严格的,不同部分不能交错放置。

限定位置形参(Python 3.8正式引入)

纯位置形参,是为了限制开头几个参数只能按位置传递。Python从3.7开始,为某些内置函数定义了这种positional-only的形参,譬如abs函数(求绝对值的) />

从它的参数提示你可以清晰地看出x是positional-only的,也就是说你不能通过abs(x=some_value)来调用它,毕竟这种参数名x并无明显含义,强用关键字形式并无好处。

从Python 3.8开始,positional-only形参将可正式用于自定义函数中,它们必须放在形参表的最前面,并在后面使用斜杠/(独占一个参数位)与普通形参分隔,比如下面这样

# a, b, c成为限定位置形参

def func_2(a, b, c, /, d):

pass

这时func_2的形参a, b, c将只能按位置接收实际参数,d仍是普通形参,可以兼容两种形式。

限定关键字形参(常叫命名关键字参数)

限定关键字形参,当然就是为了限制后面几个参数只能按关键字传递,这往往是因为后面几个形参名具有十分明显的含义,显式写出有利于可读性;或者后面几个形参随着版本更迭很可能发生变化,强制关键字形式有利于保证跨版本兼容性。

限定关键字形参(keyword-only),限制调用者不能按位置传递,需要放在形参表的后面,并在前面使用星号*(独占一个参数位)与普通形参分隔,即类似这样

def func_3(其他形参, *, kw1, kw2):

pass

这时参数kw1, kw2在函数调用时必须显式写出,即类似func_3(其他实参, kw1=var1, kw2=var2)的形式。

下面举一个三种混合形参的例子

def func_4(a, b, c, /, m, n, *, kw1, kw2):

pass

其中的m, n当然就是兼容两种形式的普通形参。

两个特殊形参

两个特殊的形参位于限定关键字形参的前后,前者紧随星号*跟它占同一位置,后者则独占最后一个参数位,并使用双星号**前缀。

前者常常取名为args(当然你取别的名也无所谓),比如

def func_5(m, n, *args, kw1, kw2):

pass

这允许args接收调用时把所有限定位置形参和普通形参都匹配完后剩余的位置实参,并封装成一个元组,你可以在函数内部通过args这个变量名使用它。如果它未接收到值则成为空元组。注意它位于普通形参之后,又只能接受位置实参,所以调用时如果希望它接收到值,前面的普通参数将也只能按位置传递;如果忽略它,普通形参倒依然能兼容关键字形式。

最后一个双星号特殊形参常常取名为kwargs(也可以取别的名字),即

def func_6(m, n, *, kw1, kw2, **kwargs):

pass

允许kwargs接收所有在调用时未成功匹配的关键字参数,并封装成一个字典。形参名变成字符串形式的键,实参成为相应键对应的值,你可以在函数内部通过kwarg这个变量使用它。不过注意由于这里面的键都是前面不存在的形参名,自由度甚高,调用者很容易不小心把前面的形参名打错,导致其被错误地传进kwargs,设计者使用它时务必谨慎。

函数调用的补充说明

再次强调,尽管函数定义的形式如此丰富,调用形式永远是之前提到的简单的前后两部分——位置实参+关键字实参。这里明确下参数传递的基本规则。

与有无默认值无关,位置实参永远按位置传递给*或*args之前对应的形参(即限定位置形参和普通形参),多余的位置实参传入*args(如果有的话),关键字实参则匹配剩下的普通形参和限定关键字形参(非限定位置形参),多余的关键字实参则传入**kwargs(如果存在的话)。

以及没有*args时,位置实参不能多于限定位置形参和普通形参的总量;

没有**kwargs时,关键字参数必须在普通形参和限定关键字形参中存在;

除*args和**kwargs外所有没有默认值的形参都必须匹配到值。

同一形参不能被匹配两次。

大多数代码编辑器,都可以为你提示出函数定义时的形参表,请确认你传入的实际参数满足条件,那么至少语法上就没问题了。出问题请自行对照以上规则进行检查。

特殊传参方法

序列解包

当你有个序列对象,想将其中元素解放出来作为调用函数的位置实参时,给它加个前缀*即可,例如你有个两个列表lst1和lst2,

lst1 = [0, 2, 1]

lst2 = [3, 5, 6]

调用如下的函数func_7,比如这样

# 这样定义

def func_7(a, b, c, /, m, n, *args, kw1, kw2):

pass

# 这样调用

func_7(*lst1, x, *lst2, kw1=y, kw2=z)

则形成了

a = lst1[0]

b = lst1[1]

c = lst2[2]

m = x

n = lst2[0]

args = (lst2[1], lst[2])

kw1=y

kw2=z

的传参效果。

注意这看起来跟定义时的*args是互逆的操作,但其实它们有很大不同——序列解包是位置实参的一部分,可以出现多次,也不限定具体位置,只要最终等效的实参表满足上面的匹配规则即可。

字典解包

当你有个字典对象,且其中的键都是合法的形参名时,你可能会想把其中的键值对解放出来作为调用函数的关键字参数,这时给它加个前缀**即可, 例如你有个两个字典dct1和dct2,

dct1 = {'kw1': 0, 'kw2': 2}

dct2 = {'n': 3, 'kw3': 5, 'kw4': 6}

调用如下的函数func_8,比如这样

# 这样定义

def func_8(a, b, c, /, m, n, *, kw1, kw2, kw3, **kwargs):

pass

# 这样调用

func_8(1, 2, 3, 4, **dct1, **dct2)

则形成了

a = 1

b = 2

c = 3

m = 4

n = dct2['n']

kw1 = dct1['kw1']

kw2 = dct1['kw2']

kw3 = dct2['kw3']

kwargs = {'kw4': dct2['kw4']}

的传参效果。

类似地,它看起来跟定义时的**kwargs是互逆的操作,但同样有很大不同——字典解包是关键字实参的一部分,可以出现多次,也不限定具体位置,只要最终等效的实参表满足上面的匹配规则即可。

形参默认值

以上章节均假设所有形参没有默认值,是为了更清晰地梳理参数匹配的关系。首先明确一点,默认值是设给形参的;其次,默认值的使用并不受限于形参究竟是位置的还是关键字的,所以像这样将它介绍为一套独立规则是合理的。尽管最常见的编码行为是为关键字形参设定默认值,但作为一套完整的规则介绍,这里必须指出这并不是一定的(竟然写出了点翻译腔……)。

默认值的设定规则极其简单:两个特殊形参*args和**kwargs不能设定默认值(或者你可以理解为它们默认值就是空元组和空字典) ;

默认值可以从限定位置形参或普通形参中的任意一个开始设定,这时须将后面剩下的所有限定位置形参和普通形参覆盖完;限定关键字形参的默认值则可以随意设定,无需考虑顺序问题。也就是说在遵循上面的形参规则的前提下,除了限定关键字形参,所有带默认值的形参必须位于无默认值的形参之后。

建议为所有限定关键字形参都设上默认值

比如下面各种形参类型最完整的func_9,可以从a, b, c, d, m, n, kw1, kw2, kw3中的任意一个开始设定默认值,直到最后。

def func_9(a, b, c, /, d, m, n, *args, kw1, kw2, kw3, **kwargs):

pass

比如

# 从限定位置形参开始设定

def func_9(a, b, c=0, /, d=1, m=2, n=3, *args, kw1=4, kw2=5, kw3=6, **kwargs):

pass

或者

# 从某个普通形参开始设定,这是最常见的做法

def func_9(a, b, c, /, d, m=0, n=1, *args, kw1=2, kw2=3, kw3=4, **kwargs):

pass

又或者

# 从限定关键字形参开始设定

def func_9(a, b, c, /, d, m, n, *args, kw1=0, kw2=1, kw3,=2 **kwargs):

pass

显然,默认值最大的作用就是允许调用者适当地忽略一些形参,但是注意,我们有必要第三次强调,调用形式还是得遵循前后两部分——位置实参+关键字实参、前后不交错的原则。由于必须匹配到所有无默认值的形参,位置实参又不具有可跳跃性,所以一般建议至早从最后一个限定位置形参开始设默认值,这样可以允许调用者自由地使用关键字参数来匹配需要传递非默认值的参数。

重要的补充说明:强烈建议使用不可变对象,如整数、浮点数、字符串、True、False、None或以上类型组成的元组等设定默认值,因为默认值只会在函数定义时被设定一次,如果是可变对象,一旦在函数内部被原地修改,效果会保留至以后每次的函数调用,不会被重新初始化。

如果非要使用某个可变对象作为默认值,比如列表,或者要设定依赖于其他参数的默认值,建议设成None,然后写成类似这样的代码

def func_10(x, default=None):

if default is None:

# 这里可以书写更复杂的初始化行为

default = []

# 剩余的函数体有默认值的形参名,最好具有明显的含义,容易让人记住。

python中一共有多少个关键字-Python 为什么会有命名关键字参数?相关推荐

  1. python中一共有多少个关键字-Python中关键字有多少个?

    Python中关键词有多少个?Python中关键词目前有31个,可以利用Python的内置的keyword模块进行输出查看. keyword模块Help on module keyword: NAME ...

  2. python中一共有多少个关键字-Python中有几个关键字

    Python中关键词有多少个?Python中关键词目前有31个,可以利用Python的内置的keyword模块进行输出查看. keyword模块 Help on module keyword: NAM ...

  3. python中一共有多少个关键字-python 查看所有的关键字

    一 查看所有的关键字:help("keywords") Here is a list of the Python keywords. Enter any keyword to ge ...

  4. python中一共有多少个关键字-python – 搜索多个关键字的字符串列表

    我有两个python列表,一个是关键字列表,另一个是文件名列表.我需要根据我拥有的关键字解析文件名列表.我希望python将文件名与关键字匹配,然后根据匹配的关键字执行操作. 我看起来像这样: key ...

  5. python中一共有多少个关键字-Python中所有的关键字

    在python中若想查询python中有哪些关键字可以先导入keyword模块 import keyword #导入关键字模块 print(keyword.kwlist) #查询所有关键字 查询结果: ...

  6. python中一共有多少个关键字-Python之33个关键字是哪些

    Python之33个关键字是:1.内置常量[False.None.True]:2.逻辑与.或.非[and or not]:3.判断与循环[if elif else,for while break co ...

  7. python 定义函数方法,python中函数如何定义?python函数的调用方法介绍

    本篇文章给大家带来的内容是关于python中函数如何定义?python函数的调用方法介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 1. 函数的概念,函数是将具有独立功能的代码块 ...

  8. linux 移除python_第16 p,PYthon中的用户交互,Python GUI编程

    大家好,我是杨数Tos,这是<从零基础到大神>系列课程的第16篇文章,第二阶段的课程:Python基础知识:PYthon中的用户交互.Python GUI编程实现方式介绍. 学习本课程,建 ...

  9. 网易之小易最近在数学课上学习到了集合的概念,集合有三个特征:1.确定性 2.互异性 3.无序性.需要根据给定的w,x,y,z,求出集合中一共有多少个元素。

    import java.util.HashSet; import java.util.Scanner; import java.util.Set;/*** 小易最近在数学课上学习到了集合的概念,集合有 ...

最新文章

  1. A Color Picker based on manifold learning
  2. linux创建sftp用户并指定访问目录,linux – 创建SFTP用户只能访问一个目录.
  3. 033_webpack打包ES6模块化工程
  4. 洛谷P3810 【模板】三维偏序(陌上花开) CDQ分治初探
  5. 漫游Kafka实战篇之搭建Kafka运行环境
  6. python分配 使最大的最小_python3中的heapq模块使用
  7. macbook pro touch bar卡死的解决方法
  8. 《大型网站架构技术》系列分享专栏
  9. python 图像的拉普拉斯变换中的数值问题_数字图像处理(第十章)
  10. “编程能力差,90%输在了数学上!”骨灰级开发:其实你们都是瞎努力!!
  11. HDU1274 展开字符串【文本处理】
  12. jQuery 实现点击页面其他地方隐藏菜单
  13. Matlab数组排序
  14. PackageInstaller (tv 修改安装app界面按钮及自动获取焦点)附源码分析
  15. 工厂如何引入ERP生产管理系统
  16. Adyen海外支付 - 直付
  17. 微信加菲猫连接服务器失败,加菲猫微信表情包
  18. 一文搞懂 STL 中 deque 与 hashtab 的底层实现
  19. 要学就学透彻!Spring Security 中 CSRF 防御源码解析
  20. 安装Python module

热门文章

  1. ES6中的异步对象Promise
  2. VMware安装CentOS6
  3. 《构建之法》需求分析 读书笔记 Week6
  4. P2340 奶牛会展(状压dp)
  5. 学写jQuery插件开发方法
  6. 数据表迁移数据一致性验证
  7. 条款39:明智而审慎的使用private继承
  8. [CF314C](Sereja and Subsequences)
  9. 浅析C# Dictionary实现原理
  10. 2017-03-02学习心得之Java代码