为什么 Python 允许参数数量错误的函数调用?

Python 是我的第一个动态语言。我最近编写了一个函数调用,错误地提供了错误数量的参数。调用该函数时出现异常,导致此操作失败。我希望即使在动态语言中,在解析源文件时也能检测到这种错误。

我理解实际参数的 类型在调用函数之前是不知道的,因为同一个变量可能在不同的时间包含任何类型的值。但是参数的 号码在解析源文件时就已经知道了。在程序运行时它不会改变。

所以这不是一个哲学问题

为了将这个问题保持在 Stack Overflow 的范围内,让我这样表述这个问题。Python 是否提供了某种特性,要求它将检查函数调用中的参数数量的时间推迟到代码实际执行时?

5724 次浏览

Python 无法预先知道您最终将调用什么对象,因为作为动态的,您可以使用 交换掉函数对象。随时都可以。每个对象可以有不同数量的参数。

下面是一个极端的例子:

import random


def foo(): pass
def bar(arg1): pass
def baz(arg1, arg2): pass


the_function = random.choice([foo, bar, baz])
print(the_function())

上面的代码有2/3的机会引发异常。但是 Python 不能事先知道是否会发生这种情况!

我甚至还没有开始使用动态模块导入、动态函数生成、其他可调用对象(可以调用任何具有 __call__方法的对象)或全部捕获参数(*args**kwargs)。

但是为了更清楚地说明这一点,你在你的问题中说:

在程序运行时它不会改变。

在 Python 中,情况并非如此,一旦加载了模块,您就可以删除、添加或替换模块命名空间中的任何对象,包括函数对象。

传递的参数数量是已知的,但是不知道实际调用的函数:

def foo():
print("I take no arguments.")


def bar():
print("I call foo")
foo()

这似乎是显而易见的,但是让我们把它们放到一个名为“ fubar.py”的文件中。现在,在一个交互式 Python 会话中,这样做:

>>> import fubar
>>> fubar.foo()
I take no arguments.
>>> fubar.bar()
I call foo
I take no arguments.

这是显而易见的。现在是有趣的部分。我们将定义一个需要非零参数的函数:

>>> def notfoo(a):
...    print("I take arguments!")
...

现在我们来做一个叫做 猴子修补程序的函数,实际上我们可以把 fubar模块中的函数 foo:

>>> fubar.foo = notfoo

现在,当我们调用 bar时,将引发一个 TypeError; 名称 foo现在引用我们上面定义的函数,而不是原来的函数 foo

>>> fubar.bar()
I call foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/horazont/tmp/fubar.py", line 6, in bar
foo()
TypeError: notfoo() missing 1 required positional argument: 'a'

因此,即使在这种情况下,很明显,被调用的函数 foo没有参数,Python 也只能知道它实际上是在执行源代码行时被调用的 foo函数。

这是 Python 的一个特性,这使得它非常强大,但也造成了它的一些缓慢。事实上,一段时间以前,python-Ideas 邮件列表上的 讨论了使模块为只读以提高性能并没有得到任何实际的支持。