Python函数调用中参数太多/太少时为什么会出现TypeError

Python函数调用中参数太多/太少时为什么会出现TypeError,python,api-design,Python,Api Design,我很难理解,当您提供的参数不是方法签名的一部分时,Python为什么会引发TypeError 例如: >>> def funky(): ... pass ... >>> funky(500) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: funky() takes no arguments

我很难理解,当您提供的参数不是方法签名的一部分时,Python为什么会引发
TypeError

例如:

>>> def funky():
...    pass
... 
>>> funky(500)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: funky() takes no arguments (1 given)
>>def funky():
...    通过
... 
>>>芬奇(500)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:funky()不接受任何参数(给定1个)
我想,如果这是因为在无参数函数的范围内,
*args
应该是
None
[]
,那么这就是一个,所以我查找了它

我发现了什么
对上的
TypeError
进行页面搜索,找到了一个上下文的正当性,在该上下文中提出了
TypeError
,但我不理解正当性。PEP的示例基本上说明了该功能基本上是
if args:raise TypeError()
的快捷方式
args
在这种情况下是非空列表,而不是空列表。。。都是同一类型的。如果我没有弄错,并且这确实是理由,那么
ValueError
可能更合适。然而,这仍然是一种漏洞百出的抽象,因为这个示例是用Python编写的,这使得它更像是某个用例的实现细节,而不是语言特性。类似于
ArgumentError
的内容在这里听起来更适合我,这让我相信,对于
TypeError
为什么有意义,我没有给出一些明显的解释。

函数使用
n
参数。您提供了
m
<代码>n!=m。这很可能是某个人代码中的错误,或者是某个人对某个API的误解的症状。此外,我认为我们无法就这种情况下应该发生什么达成有意义、有用的语义一致。是否应该忽略多余的论点?这会让这些错误通过,而且由于“错误永远不会悄悄通过”,这是不可接受的。类似地,为省略的参数提供一些默认值允许在拼写错误等情况下出现无声的错误行为,因此违反了相同的原则,更不用说“显式优于隐式”,也就是说,如果你想传递一些特殊值,你应该把它写出来

您引用的PEP及其
if args:raise TypeError(…)
语义仅适用于具有不接受varargs的纯关键字参数的函数,并在参数列表中使用普通的
*
来编码此参数。有些函数接受数量有限的位置参数,并且只使用关键字参数,但varargs对它们没有意义。对于这些,plain
*
允许只使用关键字的参数,而不需要样板代码,并且在有人提供太多参数时仍会通知程序员(这是一件好事,因为答案的第一部分列出了原因)

至于选择
TypeError
的原因:“参数数量错误”是静态语言中的编译时类型错误/类型不匹配。在Python中,我们没有compiletime检查此类内容,但它仍然是一种“类型错误”。如果(在Python中,非正式)类型签名说“我接受两个参数”,而您提供了三个参数,那么这显然违反了该约定
TypeError
不仅仅意味着
不存在(x,预期)
,就像许多其他与键入相关的主题一样(想想继承和“获取父对象”与“是父对象”),它是一个更广泛的概念

针对您对静态类型比较的批评进行编辑:考虑一元函数与二元函数的不同不仅仅是静态类型系统的一个基本限制。即使在Python这样的语言中,它也非常有用,因为这两种语言没有足够的相似性,不能“像走路和嘎嘎叫一样”使用(就像鸭子打字一样)——它们不能以相同的方式使用。(例外情况是,封装了基本算术函数的元编程,由
*
**
解包运算符处理。)它们碰巧有一个共同的事实,即它们是可调用的,但它们是不兼容的,即使Python的内置对象层次结构没有创建大量相同的类,这些类只在算术和名称上有所不同<代码>类型(f)可能会说
函数
,但这并不总是故事的结尾。

你说: def funky(): ...
所以程序“funky”不应该接受任何“参数”。但是当你使用这个程序时,你说funky(参数),funky不接受参数,所以导致了错误。要解决此问题,请使用;def funky(n):pass.

可能是因为,从语义上讲,函数/方法签名是其“类型”的一部分?@delnan-所以,这是一个泄漏的抽象,不是吗?为什么Python程序员会关心在静态语言中发生的事情(关于实际目的,而不是理论或教育)?@orokusaki:这不是一个漏洞百出的抽象。Python下面没有任何静态的东西可以泄漏这样的内容。这是一个经过深思熟虑的设计决策:函数的“类型”——在Python有类型的范围内——包括它的签名。由于我们不键入名称,此签名主要由位置参数和关键字参数的存在和数量组成,但这是函数类型的一部分。@delnan-如果它不是泄漏的抽象,那么我应该能够
type(some_function)
并获得签名参数的特定类型,否?@orokusaki:您可以检查可调用对象的签名,例如通过查看
code
对象的成员<代码>类型不这样做,因为它只提供对象的类。并且为函数签名(例如
Function2ArgsVarargsKeywordArgs\u foo\u bar
)的每一个可能的部分组合创建一个额外的类/“type object”是浪费和完全无用的。@delnan-你的回答启发了我为什么Python会这样做(我对静态相关的东西不太了解),但是你经历的越多