python 函数参数定义中的这两个分隔符,还有人不知道吗?
python 函数的参数定义想必大家应该是非常熟悉的,有两种:
位置参数(positional argument):根据函数在参数列表中的位置传递给函数的参数。
关键词参数(keyword argument):通过指定参数名称及其对应值传参的参数。
这里的 a 是位置参数,b 、c 是关键词参数。
但是,调用时,我们可以通过多种方式传参,貌似没有明确的位置(positional)或关键字(keyword)界限:
请注意:所有位置参数都必须首先出现,然后是关键字参数
在 Python 函数中,参数默认可以按位置(positional)或按关键字(keyword)传入,这意味着调用者可以基于参数的位置或名称来传递值。
然而,在某些情况下,我们可能需要对参数的传入方式进行限制,以确保函数调用的明确性和正确性。
具体来说,我们可以将某些参数指定为仅位置参数(positional only),这意味着它们只能通过位置来传入,而不是 keyword 传参;
同时,也可以将其他参数指定为仅关键字参数(keyword only),这要求它们必须通过关键字来指定。
首先,要怎么确定位置参数或关键字参数呢?
以下是确定这一点的简单经验法则:
核心参数通常是位置参数。核心参数是什么?通常指函数运行所必需的参数。
选项、标志和配置通常是关键字。这些参数通常不是函数的核心参数,而是修改函数的行为方式。
这看起来有点抽象,所以让我们看一个具体的例子。以下是标准库中 shutil 模块中 copyfile 函数的函数签名。
顾名思义,此功能将文件从一个地方复制到另一个地方。因此,src 和 dst 参数是函数的核心。
此外,还有一个标志 follow_symlinks。
此标志不是函数操作的核心部分,但它改变了函数的行为方式。因此,此参数应为关键字参数。
这样的区分有助于避免参数的混淆,并提供了更精确的函数调用传参方式。
我们发现,copyfile 函数签名中有个*参数,这又是什么呢?这将强制 follow_symlinks 只能关键字传参(keyword only)。
特殊参数
python3.8 之后,python 引入了一个新的函数定义语法,你可以使用 Special parameters(/和*)将你函数的位置参数和关键字参数分开,即可以强制执行位置参数或强制关键字参数。
一图胜千言。
简而言之就是:
位于斜杠/之前的参数被指定为仅位置参数(positional only),这意味着它们必须按照在函数定义中出现的顺序传入,不能使用关键字参数的形式。
位于星号*之后的位置则被保留给仅关键字参数(keyword only),调用者必须使用关键字来指定这些参数。
至于/和*之间的参数,它们遵循默认的行为,既可以通过位置传入,也可以通过关键字传入。
当然,在函数的定义中,这二者你也可以只用一个,或者都不用,或者两个都用。
用法示例
Python 中的强制位置函数参数/的用法
所有/之前的参数都是 positional-only 参数,这意味着它们只能作为位置参数传递给函数,而不能作为关键字参数传递。
一般,核心参数,或在参数顺序比参数名称更重要的情况下,或者参数的名字本身没什么语义(开发者希望在未来可以随时修改这个参数的名字),例如在处理图像或执行几何计算时,此功能非常有用。通过使用仅位置参数,仅需确保以正确的顺序传参调用函数,从而提高代码的清晰度和可维护性。
对于上面这个函数而言,调用 positional_only 函数时,参数 a、b 只能是位置参数,即:positional_only(1, 2)执行正确。
而 positional_only(1, b=2)和 positional_only(a=1, b=2)将执行错误。
Python 中的强制关键字函数参数*的用法
所有*之后的参数都是 keyword-only 参数,它们只能作为关键字参数传递给函数,不能作为位置参数传递。
当您想要强制对某些参数(例如具有默认值的可选参数)使用关键字参数时,或者当您想要明确区分必需参数和可选参数时,又或者这些参数在未来的位置也随时可能发生变化(在前面加入新的参数之类),此功能非常有用。
对于上面这个函数而言,调用 keyword_only 函数时,强制参数 c 只能是关键字(keyword-only)参数传参;a、b 不做限制,属于 positional or keyword 传参。
/和*都出现在函数参数中
从上面可知,强制 a 是 positional_only 参数,b(不做限制)是 positional or keyword 参数,强制 c 属于 keyword-only 参数。
内置函数中的强制位置参数和强制关键字参数
此外,这种语法也在 Python 内置函数的定义和文档说明中得到了应用,体现了其在官方实践中的规范性和重要性。如我们熟知的 exec 内置函数的函数签名:
对于此函数,参数 object 和 globals、locals 必须作为位置传递,仅因为它们出现在/的左侧。另一方面,closure 是一个仅关键字参数,因为它出现在*的右侧。
关于函数签名, exec 需要注意的一件有趣的事情是 globals 和 locals 具有默认值。因此,传递这些选项不是强制性的。但是,如果您确实为这些参数传入了值,那么它必须是位置的。显然,python 核心开发人员认为这两个参数是 exec 函数的核心部分。
再比如 sorted 函数:
iterable 必须以 positional-only 的形式传入,而两个可选参数 key 和 reverse,若传必须以 keyword-only 的形式传入。
小结
要指定函数调用者仅能使用位置参数,您可以将这些参数放置在斜杠/之前。这样,调用者在传递这些参数时必须按照它们在函数定义中出现的顺序,而不能使用关键字参数。
相反,如果您希望确保调用者在使用函数时必须显式地指定某些参数,您可以将这些参数放置在星号*之后。这要求调用者在调用函数时使用关键字参数来传递这些值,从而避免了依赖于参数的位置,提高了代码的可读性和明确性。
在设计函数时,如果您面临需要明确区分参数传入方式的语义需求,不妨考虑采用这两个分隔符/和 *。这种明确的参数定义不仅有助于减少因参数误用而导致的错误,还能增强程序的健壮性和可靠性,从而提升整体的代码质量和维护性。
作者:暴走的海鸽
链接:https://juejin.cn/post/7389147578153828392
评论