Whenever a Python function is called with missing named arguments, it produces a runtime error that lists the number of missing arguments:
TypeError: getVolume() takes exactly 3 arguments (2 given)
However, this doesn't tell me which specific arguments are missing. This runtime error message would be much more informative if it actually printed the names of the missing arguments, instead of just printing the number of arguments that are missing. This is especially important when working with functions that accept a large number of arguments: it's not always easy to remember the name of every single argument that is missing.
In general, is it possible to modify Python functions so that they will print the names of the missing arguments whenever arguments are missing?
def getVolume(length, width, height):
return length*width*height;
print(getVolume(height=3, width=3));
This has changed in Python3.3 (at most), you get the missing argument names for free:
>>> def getVolume(length, width, height):
... return length*width*height;
...
>>> print(getVolume(height=3, width=3));
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getVolume() missing 1 required positional argument: 'length'
Related
Python obviously does not allow the same keyword to appear multiple times in function arguments. Here are 2 code samples to show that point:
def foo(a=None, **kwargs):
pass
Calling this code to produce a SyntaxError:
>>> # Example 1
>>> foo(a=1, a=1)
File "<stdin>", line 1
SyntaxError: keyword argument repeated
Calling this code to produce a TypeError:
>>> # Example 2
>>> foo(1, a=123)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'a'
Intuitively, I would expect both examples to raise SyntaxErrors.
Why does the first method produce a SyntaxError while the second method produces a TypeError?
From here:
Syntax errors are the most basic type of error. They arise when the Python parser is unable to understand a line of code.
So foo(a=1, a=1) is an invalid python code, while foo(1, a=123) by itself without escope context is a valid python code, thus syntax error is not raised. However, after the code gets parsed, the interpretation of it given the current context fails because the signature of the foo function defined before confuses the interpreter since you have multiple definitions for a.
I can say that it's not a KeyError because you don't have an explicitly dictionary involved. You are passing key words arguments but a dictionary was never created and KeyError is Raised when a mapping (dictionary) key is not found in the set of existing keys. Additionally, from the documentation you linked in the comment, I would say that it's a TypeError because it involves the parsing of the arguments in a function call. But I'm not so sure. Nevertheless, the RuntimeError is a generic exception when no one of the other exceptions fit but the interpreter detected an error.
What's the recommended style for argument names?
open(file='myfile.txt')
or
open('myfile.txt')
PEP8 Style Guide For Python doesn't mention anything about this. When do I use one or the other?
The main benefit I see in using explicit names is that the modification of a function's signature will have less impact on code relying on it.
For instance, say you're using a module's function, defined as:
their_function(a, b, c=1)
You're calling it with the c keyword argument by:
their_function(myA, myB, myC)
But now, the module's developers find it useful to have another keyword argument, and in their mind, it makes more sense that it comes before c.
So now, the function is:
their_function(a, b, d=2, c=1)
Everywhere you call their_function(myA, myB, myC), now myC is passed to their_function as d, and everything is messed up.
On the other hand, had you called it by their_function(myA, myB, c=myC), the mapping of the keyword arguments would have been so that myC would have still been passed to their_function as c.
Of course, this is probably overkill for obvious functions, such as print or open whose, positional argument is natural.
But I find it really reassuring to call open(path, 'r', encoding='utf8'), rather than open(path, 'r', 'utf8'), because even if I got the order wrong, the behaviour would still be as expected.
As for me, except in a few cases where it would be counter-intuitive, I tend to force the use of the names for the keyword arguments.
Python 3, from some version on, allows you to do the following:
def my_function(a, b, *, c=1):
pass
Here, the use of the splat operator * alone tells Python that no positional argument can be found after the third one.
This will result in a TypeError when passing a fourth argument as a positional one, ie without naming it.
>>> my_function(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: my_function() missing 1 required positional argument: 'b'
>>> my_function(1, 2)
# OK
>>> my_function(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: my_function() takes 2 positional arguments but 3 were given
>>> my_function(1, 2, c=3)
# OK
This helps you make your code a little more fool-proof, especially when defining functions with many positional or keyword arguments.
You'll get different opinions. Personally I would argue that using keyword arguments whenever possible is strictly better because it attaches human readable semantics to a function call. The reader has a decent shot at guessing what the arguments are supposed to be without inspecting the code/docs any further.
However, I do sometimes omit the keyword(s) when using well-known functions.
I'm new at Python, and I'm trying to basically make a hash table that checks if a key points to a value in the table, and if not, initializes it to an empty array. The offending part of my code is the line:
converted_comments[submission.id] = converted_comments.get(submission.id, default=0)
I get the error:
TypeError: get() takes no keyword arguments
But in the documentation (and various pieces of example code), I can see that it does take a default argument:
https://docs.python.org/2/library/stdtypes.html#dict.get
http://www.tutorialspoint.com/python/dictionary_get.htm
Following is the syntax for get() method:
dict.get(key, default=None)
There's nothing about this on The Stack, so I assume it's a beginner mistake?
Due to the way the Python C-level APIs developed, a lot of built-in functions and methods don't actually have names for their arguments. Even if the documentation calls the argument default, the function doesn't recognize the name default as referring to the optional second argument. You have to provide the argument positionally:
>>> d = {1: 2}
>>> d.get(0, default=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: get() takes no keyword arguments
>>> d.get(0, 0)
0
The error message says that get takes no keyword arguments but you are providing one with default=0
converted_comments[submission.id] = converted_comments.get(submission.id, 0)
Many docs and tutorials, for instance https://www.tutorialspoint.com/python/dictionary_get.htm, erroneously specify the syntax as
dict.get(key, default = None)
instead of
dict.get(key, default)
I am working with ceilometer python API and publishing data to pubnub. not sure what is meant by this error.
This is the part of code that is causing the problem i think,
def init_Data(data, channel):
cpu_sample = cclient.samples.list(meter_name ='cpu_util')
for each in cpu_sample:
timetamp = each.timestamp
volume = each.counter_volume
volume_int = int(volume)
data_volume ={'value': volume_int}
data=json.dumps(data_volume)
print (data)
pubnub.publish(channel='orbit_channel', callback= init_Datar)
publish() takes at least 3 arguments (3 given)
Such a terrible error message! One point of confusion is that self is also counted as an argument, even if it's not explicitly provided.
So you need to provide 2 arguments. And you did! But you need to provide the 2 required arguments, while you only provided 1 required and 1 optional argument. Check the API docs for pubnub.publish() to see what you're missing.
While Daniel provided a good explanation, I wanted a minimalist example and was able to come up with this:
>>> class Foo(object):
... def __init__(self, arg1, arg2=None):
... pass
...
>>> Foo(arg2=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes at least 2 arguments (2 given)
So two arguments are provided (self and arg2), but it's saying at least 2 positional arguments are required (self and arg1). So Foo(arg1=1) would work, as would Foo(1, 2) and Foo(1, arg2=2).
I'm new at Python, and I'm trying to basically make a hash table that checks if a key points to a value in the table, and if not, initializes it to an empty array. The offending part of my code is the line:
converted_comments[submission.id] = converted_comments.get(submission.id, default=0)
I get the error:
TypeError: get() takes no keyword arguments
But in the documentation (and various pieces of example code), I can see that it does take a default argument:
https://docs.python.org/2/library/stdtypes.html#dict.get
http://www.tutorialspoint.com/python/dictionary_get.htm
Following is the syntax for get() method:
dict.get(key, default=None)
There's nothing about this on The Stack, so I assume it's a beginner mistake?
Due to the way the Python C-level APIs developed, a lot of built-in functions and methods don't actually have names for their arguments. Even if the documentation calls the argument default, the function doesn't recognize the name default as referring to the optional second argument. You have to provide the argument positionally:
>>> d = {1: 2}
>>> d.get(0, default=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: get() takes no keyword arguments
>>> d.get(0, 0)
0
The error message says that get takes no keyword arguments but you are providing one with default=0
converted_comments[submission.id] = converted_comments.get(submission.id, 0)
Many docs and tutorials, for instance https://www.tutorialspoint.com/python/dictionary_get.htm, erroneously specify the syntax as
dict.get(key, default = None)
instead of
dict.get(key, default)