Why does Python allow function calls with wrong number of arguments?

PythonArguments

Python Problem Overview


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.

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?

Python Solutions


Solution 1 - Python

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.

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!

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

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.

Solution 2 - Python

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:

>>> 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 = 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.

>>> 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.

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhelloView Question on Stackoverflow
Solution 1 - PythonMartijn PietersView Answer on Stackoverflow
Solution 2 - PythonJonas SchäferView Answer on Stackoverflow