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


80

Python是我的第一种动态语言。我最近对一个函数调用进行了编码,错误地提供了错误数量的参数。该操作失败,但在调用该函数时发生了异常。我希望即使使用动态语言,在分析源文件时也可以检测到这种错误。

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

因此,这不是一个哲学问题

为了将其保留在Stack Overflow的范围内,让我用这样的方式表达问题。Python是否提供某些功能,要求它延迟检查函数调用中的参数数量,直到代码真正执行为止?


17
如果名称在运行时完全指代另一个函数怎么办?
jonrsharpe

好吧,作为反驳,f(*args)在解析阶段将不会知道用于调用的引数。
卢卡斯Rogalski

3
应该注意的是,在类型论中,就函数而言,值的类型包括自变量的数量。
PyRulez'1

9
在实际尝试调用之前,Python如何知道要调用哪个函数?就像Python中的函数没有名称一样。(这不是讽刺)
user253751'1

1
@immibis:好吧,不。他们确实有名字。不过,这些名称并没有特别附加。定义为def foo(): whateverhas的函数foo.__name__ == 'foo',但不会阻止您执行foo = some_other_functionor bar = foo
user2357112支持Monica

Answers:


146

Python无法预先知道您最终将调用哪个对象,因为它是动态的,因此可以换出function object。随时。每个对象可以具有不同数量的参数。

这是一个极端的例子:

import random

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

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

上面的代码有三分之二的机会引发异常。但是Python无法知道先验的情况!

而且我什至还没有开始使用动态模块导入,动态函数生成,其他可调用对象(__call__可以调用带有方法的任何对象)或包罗万象的参数(*args**kwargs)。

但是,为了使这一点更加清楚,您需要提出以下问题:

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

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


我以前见过的功能表,因此它可能不是那么邪恶。哦,等等,表中的函数不使用*参数....这是Python,所以..是的,非常极端。
Ray Toal'1

3
现在是一件T恤。(如果不知道如何添加归因。如果您愿意,请告诉我,我可能可以找出原因。)
PyRulez

@PyRulez:我的带有正则表达式解析帖子独角兽形状副本的T恤使用的是URL。您可以添加https://stackoverflow.com/a/34567789并完成它。
马丁·彼得斯

4
@PyRulez:确实,这只是一个调度列表。该是要说明,你可以使用函数作为对象,你可以不知道的前期哪一个被调用。否则,这是一种相当普遍的技术
马丁·彼得斯

也许是为了帮助用户解决其最初的问题:在代码运行之前检测到错误,可以提及使用静态验证工具。
Xavier Combelle '16

35

传递的参数数量是已知的,但实际调用的函数却未知。请参阅以下示例:

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!")
...

现在我们做的事情叫做猴子修补。我们其实是可以更换的功能foofubar模块:

>>> fubar.foo = notfoo

现在,当我们调用时barTypeError将引发a;foo现在,该名称指的是我们上面定义的功能,而不是原来的功能(以前称为as-)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邮件列表中已经讨论了将模块设置为只读以提高性能,但是并没有获得任何真正的支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.