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

时间:2021-05-28 00:10:17

Python is my first dynamic language. I recently coded a function call incorrectly supplying a wrong number of arguments. This failed with an exception at the time that function was called. I expected that even in a dynamic language, this kind of error can be detected when the source file is parsed.

Python是我的第一个动态语言。我最近编写了一个函数调用错误地提供了错误数量的参数。这在调用函数时发生异常失败。我期望即使在动态语言中,也可以在解析源文件时检测到这种错误。

I understand that the type of actual arguments is not known until the function is called, because the same variable may contain values of any type at different times. But the number of arguments is known as soon as the source file is parsed. It is not going to change while the program is running.

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

So that this is not a philosophical question

To keep this in scope of Stack Overflow, let me phrase the question like this. Is there some feature, that Python offers, that requires it to delay checking the number of arguments in a function call until the code actually executes?

为了将其保持在Stack Overflow的范围内,让我说出这样的问题。是否有一些Python提供的功能,它要求它延迟检查函数调用中的参数数量,直到代码实际执行为止?

2 个解决方案

#1


143  

Python cannot know up-front what object you'll end up calling, because being dynamic, you can swap out the function object. At any time. And each of these objects can have a different number of arguments.

Python无法预先知道你最终会调用哪个对象,因为它是动态的,你可以换掉函数对象。随时。并且每个对象可以具有不同数量的参数。

Here is an extreme example:

这是一个极端的例子:

import random

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

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

The above code has a 2 in 3 chance of raising an exception. But Python cannot know a-priori if that'll be the case or not!

上面的代码有两到三次提出异常的机会。但是,如果不是这样的话,Python就无法知道先验!

And I haven't even started with dynamic module imports, dynamic function generation, other callable objects (any object with a __call__ method can be called), or catch-all arguments (*args and **kwargs).

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

But to make this extra clear, you state in your question:

但要明确这一点,请在问题中说明:

It is not going to change while the program is running.

程序运行时不会改变。

This is not the case, not in Python, once the module is loaded you can delete, add or replace any object in the module namespace, including function objects.

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

#2


33  

The number of arguments being passed is known, but not the function which is actually called. See this example:

传递的参数数量是已知的,但不是实际调用的函数。看这个例子:

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

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

This might seem obvious, but let us put these into a file called "fubar.py". Now, in an interactive Python session, do this:

这似乎是显而易见的,但让我们将这些放入名为“fubar.py”的文件中。现在,在交互式Python会话中,执行以下操作:

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

That was obvious. Now for the fun part. We’ll define a function which requires a non-zero amount of arguments:

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

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

Now we do something which is called monkey patching. We can in fact replace the function foo in the fubar module:

现在我们做一些叫做猴子修补的东西。实际上我们可以替换fubar模块中的函数foo:

>>> fubar.foo = notfoo

Now, when we call bar, a TypeError will be raised; the name foo now refers to the function we defined above instead of the original function formerly-known-as-foo.

现在,当我们调用bar时,将引发TypeError;名称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'

So even in a situation like this, where it might seem very obvious that the called function foo takes no arguments, Python can only know that it is actually the foo function which is being called when it executes that source line.

所以即使在这样的情况下,看起来很明显被调用的函数foo不带参数,Python只能知道它实际上是在执行该源代码时被调用的foo函数。

This is a property of Python which makes it powerful, but it also causes some of its slowness. In fact, making modules read-only to improve performance has been discussed on the python-ideas mailinglist some time ago, but it didn't gain any real support.

这是Python的一个属性,它使它强大,但它也导致它的一些缓慢。事实上,前面已经在python-ideas邮件列表中讨论了将模块设置为只读以提高性能,但它并没有得到任何真正的支持。

#1


143  

Python cannot know up-front what object you'll end up calling, because being dynamic, you can swap out the function object. At any time. And each of these objects can have a different number of arguments.

Python无法预先知道你最终会调用哪个对象,因为它是动态的,你可以换掉函数对象。随时。并且每个对象可以具有不同数量的参数。

Here is an extreme example:

这是一个极端的例子:

import random

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

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

The above code has a 2 in 3 chance of raising an exception. But Python cannot know a-priori if that'll be the case or not!

上面的代码有两到三次提出异常的机会。但是,如果不是这样的话,Python就无法知道先验!

And I haven't even started with dynamic module imports, dynamic function generation, other callable objects (any object with a __call__ method can be called), or catch-all arguments (*args and **kwargs).

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

But to make this extra clear, you state in your question:

但要明确这一点,请在问题中说明:

It is not going to change while the program is running.

程序运行时不会改变。

This is not the case, not in Python, once the module is loaded you can delete, add or replace any object in the module namespace, including function objects.

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

#2


33  

The number of arguments being passed is known, but not the function which is actually called. See this example:

传递的参数数量是已知的,但不是实际调用的函数。看这个例子:

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

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

This might seem obvious, but let us put these into a file called "fubar.py". Now, in an interactive Python session, do this:

这似乎是显而易见的,但让我们将这些放入名为“fubar.py”的文件中。现在,在交互式Python会话中,执行以下操作:

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

That was obvious. Now for the fun part. We’ll define a function which requires a non-zero amount of arguments:

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

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

Now we do something which is called monkey patching. We can in fact replace the function foo in the fubar module:

现在我们做一些叫做猴子修补的东西。实际上我们可以替换fubar模块中的函数foo:

>>> fubar.foo = notfoo

Now, when we call bar, a TypeError will be raised; the name foo now refers to the function we defined above instead of the original function formerly-known-as-foo.

现在,当我们调用bar时,将引发TypeError;名称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'

So even in a situation like this, where it might seem very obvious that the called function foo takes no arguments, Python can only know that it is actually the foo function which is being called when it executes that source line.

所以即使在这样的情况下,看起来很明显被调用的函数foo不带参数,Python只能知道它实际上是在执行该源代码时被调用的foo函数。

This is a property of Python which makes it powerful, but it also causes some of its slowness. In fact, making modules read-only to improve performance has been discussed on the python-ideas mailinglist some time ago, but it didn't gain any real support.

这是Python的一个属性,它使它强大,但它也导致它的一些缓慢。事实上,前面已经在python-ideas邮件列表中讨论了将模块设置为只读以提高性能,但它并没有得到任何真正的支持。