This question already has answers here:
Can one partially apply the second argument of a function that takes no keyword arguments?
(13 answers)
Closed 5 years ago.
Trying something out with partial, I observed the following behaviour:
First, I defined a function foo which takes 2 non-keyword arguments:
>>> def foo(salutation, name):
... print(salutation, name)
Then, I use partial to create a higher order wrapper.
>>> from functools import partial
>>> f = partial(foo, name='Coldspeed')
Then, called it like this:
>>> f('Hey')
This gave me Hey Coldspeed which was expected. Next, I tried to apply partial to isinstance:
>>> f = partial(isinstance, classinfo=str)
>>> f('hello')
But this gave me an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: isinstance() takes no keyword arguments
I tried classinfo as the argument name since that was what was specified in the docs (linked above). It seems that the actual definition does not have the second argument named classinfo as the docs might otherwise state.
How now, would I create a partial function with the second parameter without being forced to specify the argname? I definitely cannot do f = partial(isinstance, str) because str will be taken as the first argument, not the second. If possible, would like to find a way to do this without using lambda functions too.
Unfortunately, that's just not something functools.partial supports. You'd need to write your own version that does support it, or find one in some 3rd-party library somewhere.
Related
This question already has answers here:
Bare asterisk in function parameters?
(6 answers)
Closed 4 years ago.
I met a weird case in the function definition in python, I read some code like this:
def abc(dd, *, ee=None):
print(dd, ee)
In the beginning, I thought this code is wrong and maybe a typo of *args, but recently I tried this code in latest python3.7, and it seems that it can be interpreted, and usage is also super wired, you can't pass more than 1 argument to this function:
>>> abc(11, 222)
Traceback (most recent call last):
File "<input>", line 1, in <module>
abc(11, 222)
TypeError: abc() takes 1 positional argument but 2 were given
>>> abc(11)
11 None
I am asking because I don't know the purpose of why some one wrote like this, and why python support this behaviour in Python3(not supported in python2)
It seems your function has 1 positional argument and one named argument. The * eats all other positional arguments, just like an *args would, but you cannot reference them.
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)
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)