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

发布于 2024-11-10 17:14:10 字数 984 浏览 1 评论 0原文

我无法理解为什么当您提供不属于方法签名的参数时 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[],这是一个泄漏抽象,所以我查了一下。

我发现了什么

PEP-3102 上搜索 TypeError 发现似乎是引发 TypeError 的一个上下文的理由,但我不明白其理由。 PEP 的示例本质上是在说明该功能基本上是 if args: raise TypeError() 的快捷方式。在这种情况下,args 是一个非空列表,而不是一个空列表……它们都是相同的类型。如果我没有弄错的话,并且这确实是合理的,那么也许 ValueError 会更合适。然而,这仍然是一种有漏洞的抽象,因为该示例是用 Python 编写的,这使其更多地是特定用例的实现细节,而不是语言功能。像 ArgumentError 这样的东西对我来说听起来更合适,这让我相信有一些明显的解释我错过了为什么 TypeError 有意义。

I'm having trouble understanding why Python raises a TypeError when you provide arguments that aren't part of a method signature.

Example:

>>> def funky():
...    pass
... 
>>> funky(500)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: funky() takes no arguments (1 given)

I thought, if this is because *args is expected to be None or [] within the scope of a no-arg function, that's a leaky abstraction, so I looked it up.

What I found

A page search for TypeError on PEP-3102 found what appeared to be justification for one context in which TypeError is raised, but I don't understand the justification. The PEP's example is essentially stating that the functionality is basically a shortcut for if args: raise TypeError(). args is a non-empty list in that case as opposed to an empty list... which are both of the same type. If I'm not mistaken and that is indeed the justification, perhaps a ValueError would be more appropriate. However, that would still be sort-of a leaky abstraction since the example is written in Python, making it more of an implementation detail of a certain use case than a language feature. Something like ArgumentError sounds much more appropriate here to me, which leads me to believe there is some obvious explanation that I missed as to why TypeError makes sense.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

余厌 2024-11-17 17:14:10

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

您引用的 PEP 和它的 if args: raise TypeError(...) 语义仅适用于具有仅关键字参数的函数,这些参数不接受可变参数并使用普通的 * 在参数列表中对此进行编码。有些函数接受有限数量的位置参数,并且可以从仅关键字参数中受益,但可变参数对它们来说没有意义。对于这些,纯 * 的存在是为了允许仅关键字参数,而不需要样板代码,并且仍然通知程序员,以防有人提供太多参数(出于第一部分中列出的原因,这是一件好事)回答)。

至于为什么选择TypeError:“参数数量错误”是静态语言中的编译时类型错误/类型不匹配。我们在 Python 中没有对此类事情进行编译时检查,但这仍然是一种“类型错误”。如果(在 Python 中,非正式的)类型签名说“我接受两个参数”,而您提供了三个,那么这显然违反了该约定。 TypeError 不仅仅意味着 not isinstance(x, Expected),就像许多其他与类型相关的主题一样(只需想想继承和“获取父级的东西”与“is-父母”)这是一个更广泛的概念。

编辑以回应您对静态类型比较的批评:考虑一元函数与二元函数不同不仅仅是静态类型系统的一个任意限制。即使在像 Python 这样的语言中,它也非常有用,因为两者不够相似,无法“就好像它们走路和嘎嘎一样”(如鸭子打字) - 它们不能以相同的方式使用。 (例外情况是包装任意数量函数的元编程,由 *** 解包运算符处理。)它们碰巧共享这样一个事实:它们是可调用的,但即使 Python 的内置对象层次结构没有创建大量仅在数量和名称上不同的相同类,它们也是不兼容的。 type(f) 可能会说 function,但这并不总是故事的结局。

The function takes n arguments. You provided m. n != m. That's propably a bug in someone's code or a symptom of someone's misunderstanding of some API. Also, I don't think we could agree on meaningful, useful semantics what should happen in this case. Should superfluous arguments be ignored? That would let such bugs pass, and since "errors shall never pass silently", that's not acceptable. Similarily, supplying some default value for omitted arguments permits silent misbehaviour in the case of typos etc. and thus violates the same principle, not to mention that "explicit is better than implicit", i.e. if you want to pass some special value you should just write that out.

The PEP you refer to and it's if args: raise TypeError(...) semantics apply only to functions with keyword-only arguments that don't accept varargs and use a plain * in the parameter list to codify this. There are functions that accept a limited number of positional arguments and would benefit from keyword-only arguments but varargs wouldn't be meaningful for them. For these, plain * exists to allow keyword-only arguments without requiring boilerplate code and still notify the programmer in case someone provides too many arguments (which is a good thing for reasons listed in the first part of the answer).

As for why TypeError was chosen: "Wrong number of arguments" is a compiletime type error/type mismatch in static languages. We don't have compiletime checking for such things in Python, but it's still kind of a "type error". If the (in Python, informal) type signature says "I take two arguments" and you provide three, that's obviously a violation of that contract. TypeError doesn't just mean not isinstance(x, expected), like much other typing-related topics (just think inheritance and "gets parent's stuff" vs. "is-a parent") it's a much broader concept.

Edit in response to your criticism of the static typing comparision: Considering an unary function different from a binary function isn't just an abritary limitation of static type systems. It's very useful even in languages like Python, since the two aren't similar enough to be used "as if they walked and quacked alike" (as in duck typing) - they can't be used in the same way. (The exception, metaprogramming which wraps functions of abritary arity, is handled by the * and ** unpacking operators.) They happen to share the fact that they're callable, but they're incompatible even if Python's builtin object hierarchy doesn't create numerous identical classes that only differ in arity and thus name. type(f) may say function, but that's not always the end of the story.

三生路 2024-11-17 17:14:10

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

You said:
def funky():
...
So the program "funky" should not take any 'arguments'. But when you used the program, you said funky(argument), and funky does not take arguments, so that caused the error. To fix this, use; def funky(n): pass .

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文