社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

python函数参数定义中的这两个分隔符,还有人不知道吗?

未闻Code • 1 年前 • 173 次点击  

今天我们来聊下python(3.8+)函数定义中的两个特殊参数/*

前言

python 函数的参数定义想必大家应该是非常熟悉的,有两种:

  • 位置参数(positional argument):根据函数在参数列表中的位置传递给函数的参数。
  • 关键词参数(keyword argument):通过指定参数名称及其对应值传参的参数。
def foo(a, b=1, c=2):
    print(a, b, c)

这里的a是位置参数,bc是关键词参数。

但是,调用时,我们可以通过多种方式传参,貌似没有明确的位置(positional)或关键字(keyword)界限:

foo(123)

foo(1, b=2, c=3)
foo(a=1, b=2, c=3)
foo(a=1, c=3, b=2)
foo(c=3, a=1, b=2)

请注意:所有位置参数都必须首先出现,然后是关键字参数

在Python函数中,参数默认可以按位置(positional)或按关键字(keyword)传入,这意味着调用者可以基于参数的位置或名称来传递值。

然而,在某些情况下,我们可能需要对参数的传入方式进行限制,以确保函数调用的明确性和正确性。

  • 具体来说,我们可以将某些参数指定为仅位置参数(positional only),这意味着它们只能通过位置来传入,而不是keyword传参;

  • 同时,也可以将其他参数指定为仅关键字参数(keyword only),这要求它们必须通过关键字来指定。


首先,要怎么确定位置参数或关键字参数呢?

以下是确定这一点的简单经验法则:

  • 核心参数通常是位置参数。核心参数是什么?通常指函数运行所必需的参数。
  • 选项、标志和配置通常是关键字。这些参数通常不是函数的核心参数,而是修改函数的行为方式

这看起来有点抽象,所以让我们看一个具体的例子。以下是标准库中shutil模块中copyfile函数的函数签名。

def copyfile(src, dst, *, follow_symlinks=True)

顾名思义,此功能将文件从一个地方复制到另一个地方。因此,srcdst参数是函数的核心。

此外,还有一个标志follow_symlinks

此标志不是函数操作的核心部分,但它改变了函数的行为方式。因此,此参数应为关键字参数。

这样的区分有助于避免参数的混淆,并提供了更精确的函数调用传参方式。


我们发现,copyfile函数签名中有个*参数,这又是什么呢?这将强制follow_symlinks只能关键字传参(keyword only)。

特殊参数

python3.8 之后,python 引入了一个新的函数定义语法,你可以使用Special parameters/*)将你函数的位置参数和关键字参数分开,即可以强制执行位置参数或强制关键字参数

一图胜千言。

简而言之就是:

  • 位于斜杠/之前的参数被指定为仅位置参数(positional only),这意味着它们必须按照在函数定义中出现的顺序传入,不能使用关键字参数的形式。
  • 位于星号*之后的位置则被保留给仅关键字参数(keyword only),调用者必须使用关键字来指定这些参数。
  • 至于/*之间的参数,它们遵循默认的行为,既可以通过位置传入,也可以通过关键字传入。
  • 当然,在函数的定义中,这二者你也可以只用一个,或者都不用,或者两个都用。

用法示例

Python 中的强制位置函数参数/的用法

所有/之前的参数都是positional-only参数,这意味着它们只能作为位置参数传递给函数,而不能作为关键字参数传递。

一般,核心参数,或在参数顺序比参数名称更重要的情况下,或者参数的名字本身没什么语义(开发者希望在未来可以随时修改这个参数的名字),例如在处理图像或执行几何计算时,此功能非常有用。通过使用仅位置参数,仅需确保以正确的顺序传参调用函数,从而提高代码的清晰度和可维护性。

def positional_only(a, b, /):
    print(a + b)


positional_only(12)

对于上面这个函数而言,调用positional_only函数时,参数a、b只能是位置参数,即:positional_only(1, 2)执行正确。

positional_only(1, b=2)positional_only(a=1, b=2)将执行错误。

# 执行将报错:TypeError: positional_only() got some positional-only arguments passed as keyword arguments: 'b'
# positional_only(1, b=2)

# 执行将报错:TypeError: positional_only() got some positional-only arguments passed as keyword arguments: 'a, b'
# positional_only(a=1, b=2) 

Python 中的强制关键字函数参数*的用法

所有* 之后的参数都是keyword-only参数,它们只能作为关键字参数传递给函数,不能作为位置参数传递。

当您想要强制对某些参数(例如具有默认值的可选参数)使用关键字参数时,或者当您想要明确区分必需参数和可选参数时,又或者这些参数在未来的位置也随时可能发生变化(在前面加入新的参数之类),此功能非常有用。

def keyword_only(a, b, *, c):
    print(a + b + c)


keyword_only(12, c=7)
keyword_only(1, b=2, c=7)
keyword_only(a=1, b=2, c=7)

对于上面这个函数而言,调用keyword_only函数时,强制参数c只能是关键字(keyword-only)参数传参;a、b不做限制,属于positional or keyword传参。

/*都出现在函数参数中

def f(a, /, b, *, c):
    print(a + b + c)


f(12, c=7)  # 10
f(1, b=2, c=7)  # 10
# f(a=1, b=2, c=7)  # wrong, TypeError: f() got some positional-only arguments passed as keyword arguments: 'a'

从上面可知,强制apositional_only参数,b(不做限制)是positional or keyword参数,强制c 属于keyword-only参数。

内置函数中的强制位置参数和强制关键字参数

此外,这种语法也在 Python 内置函数的定义和文档说明中得到了应用,体现了其在官方实践中的规范性和重要性。如我们熟知的exec内置函数的函数签名:

exec(object, globals=None, locals=None, /, *, closure=None)
 # help(exec) 可查看函数签名

对于此函数,参数objectglobalslocals必须作为位置传递,仅因为它们出现在/的左侧。另一方面,closure是一个仅关键字参数,因为它出现在*的右侧。

def keyword_only(*, a, b, c):
    print(a + b + c)


exec("keyword_only(a=1, b=2, c=7)")

关于函数签名, exec需要注意的一件有趣的事情是globalslocals具有默认值。因此,传递这些选项不是强制性的。但是,如果您确实为这些参数传入了值,那么它必须是位置的。显然,python 核心开发人员认为这两个参数是exec函数的核心部分。

再比如sorted函数:

sorted(iterable, /, *, key=None, reverse=False)

iterable必须以positional-only的形式传入,而两个可选参数keyreverse,若传必须以keyword-only的形式传入。

data = {'apple'2'orange'4'banana'1}
# 按字典的值排序
sorted_data_by_value = sorted(data.items(), key=lambda item: item[1], reverse=True)
print(sorted_data_by_value)  # 输出: [('orange', 4), ('apple', 2), ('banana', 1)]

小结

要指定函数调用者仅能使用位置参数,您可以将这些参数放置在斜杠/之前。这样,调用者在传递这些参数时必须按照它们在函数定义中出现的顺序,而不能使用关键字参数。

相反,如果您希望确保调用者在使用函数时必须显式地指定某些参数,您可以将这些参数放置在星号*之后。这要求调用者在调用函数时使用关键字参数来传递这些值,从而避免了依赖于参数的位置,提高了代码的可读性和明确性。

在设计函数时,如果您面临需要明确区分参数传入方式的语义需求,不妨考虑采用这两个分隔符/*。这种明确的参数定义不仅有助于减少因参数误用而导致的错误,还能增强程序的健壮性和可靠性,从而提升整体的代码质量和维护性。

更多每日开发小技巧

尽在未闻 Code Telegram Channel !


END

未闻 Code·知识星球开放啦!

一对一答疑爬虫相关问题

职业生涯咨询

面试经验分享

每周直播分享

......

未闻 Code·知识星球期待与你相见~

一二线大厂在职员工

十多年码龄的编程老鸟

国内外高校在读学生

中小学刚刚入门的新人

“未闻 Code技术交流群”等你来!

入群方式:添加微信“mekingname”,备注“粉丝群”(谢绝广告党,非诚勿扰!)

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/172291
 
173 次点击