当您提供不属于方法签名的参数时,我无法理解为什么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)
我想,如果这是因为*args
在无参数函数范围内应该是None
或[]
,那就是leaky abstraction,所以我抬起头来。
在PEP-3102上对TypeError
进行网页搜索,发现似乎是TypeError
引发的一个上下文的理由,但我不明白其理由。 PEP的示例基本上说明功能基本上是if args: raise TypeError()
的快捷方式。在这种情况下,args
是非空列表,而不是空列表...它们都是相同的类型。如果我没有弄错, 确实是理由,那么ValueError
也许更合适。但是,这仍然是一种漏洞抽象,因为该示例是用Python编写的,这使得它更像是某个用例的实现细节,而不是语言特性。像ArgumentError
这样的东西对我来说听起来更合适,这让我相信有一些明显的解释,我错过了为什么TypeError
有意义。
答案 0 :(得分:0)
该函数接受n
个参数。您提供了m
。 n != m
。这可能是某人代码中的错误或某人误解某些API的症状。此外,我认为我们不同意有意义的,有用的语义,在这种情况下会发生什么。多余的论点应该被忽略吗?这会让这些错误传递,并且由于“错误永远不会无声地传递”,这是不可接受的。类似地,为省略的参数提供一些默认值允许在拼写错误的情况下出现无声的错误行为,因此违反了相同的原则,更不用说“明确比隐含更好”,即如果你想传递一些特殊的价值,你应该只是把它写出来。
您引用的PEP及其if args: raise TypeError(...)
语义仅适用于不接受varargs的关键字参数的函数,并在参数列表中使用普通*
来编码。有些函数可以接受有限数量的位置参数,并且可以从仅限关键字的参数中受益,但varargs对它们没有意义。对于这些,普通*
存在以允许仅关键字参数而不需要样板代码,并且仍然通知程序员以防有人提供太多参数(这对于答案的第一部分中列出的原因是一件好事)。
至于选择TypeError
的原因:“参数数目错误”是静态语言中的编译时类型错误/类型不匹配。我们没有在Python中对这些东西进行编译时检查,但它仍然是一种“类型错误”。如果(在Python,非正式)类型签名中说“我接受两个参数”而你提供三个,那显然违反了该合同。 TypeError
并不仅仅意味着not isinstance(x, expected)
,就像许多其他与打字相关的主题一样(只是认为继承和“得到父母的东西”而不是“是 - 父母”)这是一个更广泛的概念。
编辑以回应您对静态类型比较的批评:考虑与二元函数不同的一元函数不仅仅是静态类型系统的一个限制性限制。它甚至在像Python这样的语言中也非常有用,因为它们两者的相似性不足以“好像它们一样走路和嘎嘎叫”(如在鸭子打字中) - 它们不能以相同的方式使用。 (例外,包含abritary arity功能的元编程由*
和**
解包操作符处理。)他们碰巧分享了他们可以调用的事实,但即使它们也不兼容Python的内置对象层次结构不会创建许多相同的类,这些类只在arity和name中有所不同。 type(f)
可能会说function
,但这并不总是故事的结尾。
答案 1 :(得分:0)