我是Python语言的新手,但我有点沮丧。
直到今天,我认为在函数调用中传递参数名称不是强制性的。例如,如果您具有以下功能:
def computeRectangleArea(width=7, height=8):
return width * height
我认为您可以像这样computeRectangleArea(width=7,height=8)
进行调用,只是为了使参数的含义更清楚,但是实际上不需要输入参数的关键字,因此您也可以通过这种方式调用相同的函数:{{ 1}}
今天,当使用computeRectangleArea(7, 8)
时,我意识到在调用此函数时必须使用openpyxl.styles.PatternFill()
关键字。
假设您以这种方式调用函数:fill_type
,那么输入参数的解释将是错误的。
我对OOP语言(Java,C#)有一定的经验,而这些都不存在。
在我看来,某些参数名称(例如上例中的openpyxl.styles.PatternFill('FFFFFF','FFFFFF','solid')
和start_color
是可选的,而其他参数(例如end_color
)则是可选的,这在我看来似乎是不一致的行为值。
有人可以向我解释为什么这项看似奇怪的政策吗?此外,如果有人可以向我指出一些有用的资源以了解其实现方式,我将感到非常高兴。
答案 0 :(得分:2)
有人可以向我解释为什么我们需要这种“头痛”…
对于您的特定示例,似乎没有任何仅关键字参数。相反,您尝试传递第一个,第二个和第四个参数的参数,而不必在您不关心的中间传递参数。
换句话说,这根本不是头痛。您可以很轻松地忽略它,这是一种便利(以及健全性检查),但可能不想这么做。
代替此:
PatternFill('FFFFFF', 'FFFFFF', fill_type='solid')
…您可以写下:
PatternFill('FFFFFF', 'FFFFFF', Color(), 'solid')
…但是要知道这是您需要发送的内容,您需要阅读源代码或文档以查看整个参数列表,并查看参数的默认值是什么您想跳过,并将其明确添加到您的通话中。
我怀疑有人会觉得更好。
而且,正如许多人在评论中指出的那样,这几乎完全是命名参数在C#中的工作方式。
这个类偶然是一个很好的示例,说明了为什么Python实际上 允许仅使用关键字的参数,即使此处未使用它们。
您可以写PatternFill('FFFFFF', 'FFFFFF', 'solid')
而不会因为TypeError
的错误参数而得到PatternFill
的事实,而是关于'solid'
不能作为颜色的神秘错误,并不是一件好事。而且(至少没有类型提示注释,这种类型的注释没有),您的IDE或任何其他工具都无法捕捉到该错误。
实际上,通过不使用关键字,您甚至在没有意识到的情况下就将初始参数弄错了。您几乎肯定想这样做:
PatternFile(None, 'FFFFFF', 'FFFFFF')
…但是您却没有看到明显的错误:
PatternFile('FFFFFF', 'FFFFFF')
…,这意味着您要传递前景色作为图案类型,将背景色传递为前景色,并保留默认背景色。
可以通过使所有或大多数参数仅使用关键字来解决。但是如果没有仅关键字的参数,唯一的选择就是**kwargs
,而这种权衡通常是不值得的。
引用PEP 3102的原理,该提议在语言中添加了仅关键字参数:
在很多情况下,函数需要采用可变数量的参数。 Python语言使用'varargs'语法(* name)来支持此功能,该语法指定将任何'剩余'参数作为元组传递给varargs参数。
对此的一个限制是,当前必须在vararg插槽之前填充所有常规参数插槽。
这并不总是可取的。可以很容易地预想一个函数,该函数接受可变数量的参数,但也可以采用关键字参数形式的一个或多个“选项”。当前,唯一的方法是同时定义varargs参数和'keywords'参数(
**kwargs
),然后从字典中手动提取所需的关键字。
如果不清楚,为什么使用*args
和**kwargs
还不够好:
help
或自动生成的文档中查看函数定义时,看不到函数的实际签名。inspect
模块或类似模块的动态反射代码也无法使用签名。args
和kwargs
访问分散在函数主体中。 有关此功能允许使用的示例,请考虑内置的print
函数,您可以这样调用它:
print(x, y, z, sep=', ')
之所以有效,是因为print
的定义如下:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False):
如果不是关键字参数,则无法将sep
传递为与要打印的实际值不同的内容。
您可以强迫用户将所有对象作为一个元组传递,而不是作为单独的参数传递,但这将大大降低友好性,即使您这样做,也无法传递{{1 }},而不会传递所有flush
,sep
和end
的值。
而且,即使有关键字参数,如果不是仅关键字参数,函数签名也必须像这样:
file
...这将使您很难确定可以传递哪些关键字参数。
答案 1 :(得分:2)
位置和关键字参数的工作方式与您更了解的语言一样。您需要转到所用方法的文档并查看签名。要创建PatternFill
对象,请转到类的__init__
方法。
class PatternFill(Fill):
def __init__(self, patternType=None, fgColor=Color(), bgColor=Color(),
fill_type=None, start_color=None, end_color=None):
您可以指定不带关键字的参数,只要按顺序提供所有参数即可,而不会跳过任何参数。例如,您的失败呼叫可以合法地表示为:
PatternFill(None, 'FFFFFF', 'FFFFFF', 'solid')
这些将与前四个参数匹配。每当您无序提供参数时,您必须提供该参数以及该调用中所有以后参数的关键字。例如,在上述调用中,如果要让其样式默认为None
,则必须为所提供的三个参数提供关键字。如果您只是省略了None
,则解析器仍会尝试从前开始依次对其进行匹配:
patternType <= 'FFFFFF'
fgColor <= 'FFFFFF'
bgColor <= 'solid'
...,您的呼叫无法通过解析。
这样可以使事情变得更清楚吗?