Python是我的第一个动态语言.我最近编写了一个函数调用错误地提供了错误数量的参数.这在调用函数时发生异常失败.我期望即使在动态语言中,也可以在解析源文件时检测到这种错误.
我知道在调用函数之前不知道实际参数的类型,因为同一个变量可能包含不同时间的任何类型的值.但是一旦解析了源文件,就会知道参数的数量.程序运行时不会改变.
为了将其保持在Stack Overflow的范围内,让我说出这样的问题.Python提供了一些功能,它要求它延迟检查函数调用中的参数数量,直到代码实际执行为止?
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())
上面的代码有两到三次提出异常的机会.但是,如果不是这样的话,Python就无法知道先验!
我甚至还没有开始使用动态模块导入,动态函数生成,其他可调用对象(__call__
可以调用任何带有方法的对象),或者包含catch-all参数(*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!")
...
现在我们做一些叫做猴子修补的东西.我们其实是可以更换的功能foo
中fubar
模块:
>>> fubar.foo = notfoo
现在,当我们打电话时bar
,TypeError
会提出一个; foo
现在这个名称指的是我们上面定义的函数,而不是以前称为原始函数的函数foo
.
>>> fubar.bar()
I call foo
Traceback (most recent call last):
File "", line 1, in
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邮件列表中讨论了将模块设置为只读以提高性能,但它并没有得到任何真正的支持.