Python函数中的输入参数

时间:2018-07-30 20:57:12

标签: python function parameters keyword-argument

我是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)则是可选的,这在我看来似乎是不一致的行为值。

有人可以向我解释为什么这项看似奇怪的政策吗?此外,如果有人可以向我指出一些有用的资源以了解其实现方式,我将感到非常高兴。

2 个答案:

答案 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模块或类似模块的动态反射代码也无法使用签名。
  • 签名也不适用于静态反射代码,就像许多IDE用于完成和建议的签名一样。
  • 该函数的实现尚不清楚,因为充其量只是提取和测试参数的一半样板,最糟糕的是argskwargs访问分散在函数主体中。

有关此功能允许使用的示例,请考虑内置的print函数,您可以这样调用它:

print(x, y, z, sep=', ')

之所以有效,是因为print的定义如下:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False):

如果不是关键字参数,则无法将sep传递为与要打印的实际值不同的内容。

您可以强迫用户将所有对象作为一个元组传递,而不是作为单独的参数传递,但这将大大降低友好性,即使您这样做,也无法传递{{1 }},而不会传递所有flushsepend的值。

而且,即使有关键字参数,如果不是仅关键字参数,函数签名也必须像这样:

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'

...,您的呼叫无法通过解析。

这样可以使事情变得更清楚吗?