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.
Related
I'm quite new to Python and I'm studing an opensource framework written in Python. I'm trying to dive a little bit deeper in the source code. I can't understand why the "Arbitrary Arguments" and "Arbitrary Keyword Arguments" are required in this line of code:
observerCallback = lambda *args, **kwargs: self.pushRender(realViewId)
basically because, at the end, they are not used in the "called" method:
def pushRender(self, vId, ignoreAnimation = False):
...
So, once again: what is the purpose of using *args and **kwargs here? I know it could looks like a silly question, but I just learnt right now meaning of this "special operators" and my brain is almost out of work after days spent on exploring this source code. If someone can help me understand a little bit better, for sure I really appreciate that.
The caller of observerCallback is probably passing some arguments to the function, so the function needs to accept them. Otherwise this would happen:
>>> observerCallback = lambda: self.pushRender(realViewId)
>>> observerCallback('foo', bar='baz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got an unexpected keyword argument 'bar'
This particular observerCallback simply chooses to ignore/not use any passed arguments. By convention you should use _ instead of "args" and "kwargs" as an indicator that you're going to disregard those arguments:
lambda *_, **__: ...
I've found a number of questions and answers dealing with determining the parameters/arguments needed for functions* in python. Python has a number of callable objects (using callable) that will return a value for which inspect.getfullargspec returns an error (inspect.Signature seems less reliable by my informal testing?).
For example:
inspect.getfullargspec(max)
Returns an Error:
TypeError: unsupported callable
Is there an alternative to inspect that will work for otherwise "unsupported callable" [functions/objects?]
Looking at a number of builtin callable objects it seems like many are in the "unsupported" category, i can only guess at objects in other modules/libraries.
*I may be mincing my terms here, apologies - any way to determine specifically which is which?
In CPython, builtin functions are implemented in C, and thus have many quirks. This is one of them. Taking some examples from PEP 570:
In current versions of Python, many CPython "builtin" and standard
library functions only accept positional-only parameters. The
resulting semantics can be easily observed by calling one of these
functions using keyword arguments:
>>> help(pow)
...
pow(x, y, z=None, /)
...
>>> pow(x=5, y=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pow() takes no keyword arguments
pow() expresses that its parameters are positional-only via the / marker.
However, this is only a documentation convention; Python developers
cannot use this syntax in code.
There are functions with other interesting semantics:
range(), an overloaded function, accepts an optional parameter to the
left of its required parameter.
dict(), whose mapping/iterator
parameter is optional and semantically must be positional-only. Any
externally visible name for this parameter would occlude that name
going into the **kwarg keyword variadic parameter dict.
Currently (while the PEP is not implemented) Python has no official way to express these unusual semantics such as positional only arguments.
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)
People mentioned in answers a1, a2 that
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.
I found it really annoying cause I'm not be able to know it by looking at the doc. For instance, eval
eval(expression, globals=None, locals=None)
Then I wrote this line of code
print(eval('a+b', globals={'a':1, 'b':2}))
and got TypeError: eval() takes no keyword arguments. So is there a complete list of functions of this kind? How do I know if a function is allowed to have keyword arguments?
In Python 3.5 you can inspect the __text_signature__ of the built-in function:
>>> eval.__text_signature__
'($module, source, globals=None, locals=None, /)'
or
>>> abs.__text_signature__
'($module, x, /)'
>>> abs(x=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: abs() takes no keyword arguments
(x cannot be used as a keyword argument)
The / means that the arguments following that can be used as keyword arguments. C.f.
>>> compile.__text_signature__
'($module, /, source, filename, mode, flags=0,\n dont_inherit=False, optimize=-1)'
>>> compile(source='foo', filename='bar', mode='exec')
<code object <module> at 0x7f41c58f0030, file "bar", line 1>
Of course there are bugs even in 3.5:
>>> sorted.__text_signature__
'($module, iterable, key=None, reverse=False)'
though according to issue 26729 in the Python bug tracker, there ought to be / after the iterable as the iterable cannot be used as a keyword argument.
Unfortunately this information is not yet available in the Python documentation itself.
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)