Why some built-in functions do not have keyword arguments [duplicate] - python

In the python documenation, it says:
Any function argument, no matter non-optional or optional (with default value) can be called as keyword argument as long as one of the argument names matches. Keyword argument, however, must follow all positional arguments.
I tried this out:
kwargs = {'step':-1, 'start':10, 'stop':5}
list(range(**kwargs))
But python gives men an error:
TypeError: range() takes no keyword arguments
Why is this?

range() is not a python function. It is a C type; C types follow different rules for arguments and range() only accepts positional arguments.
See the Calls expressions documentation:
CPython implementation detail: An implementation may provide built-in functions whose positional parameters do not have names, even if they are ‘named’ for the purpose of documentation, and which therefore cannot be supplied by keyword. In CPython, this is the case for functions implemented in C that use PyArg_ParseTuple() to parse their arguments.
The positional parameters of range() are not named so cannot be used as keyword arguments.

As you know by now the real answer is that range is a C function which for some reason does not have the same rules of python (would be nice to know why).
But you can do this instead:
range(*{'start':0,'stop':10,'step':2}.values())

Related

Is there any simple way to pass arguments based on their position, rather than kwargs. Like a positional version of kwargs?

Is there a generic python way to pass arguments to arbitrary functions based on specified positions? While it would be straightforward to make a wrapper that allows positional argument passing, it would be incredibly tedious for me considering how frequently I find myself needing to pass arguments based on their position.
Some examples when such would be useful:
when using functools.partial, to partially set specific positional arguments
passing arguments with respect to a bijective argument sorting key, where 2 functions take the same type of arguments, but where their defined argument names are different
An alternative for me would be if I could have every function in my code automatically wrapped with a wrapper that enables positional argument passing. I know several ways this could be done, such as running my script through another script which modifies it, but before resorting to that I'd like to consider simpler pythonic solutions.
For key arguments use **kwargs but for positional arguments use *args.

TypeError: 'x' is an invalid keyword argument for int() [duplicate]

I came across this - in my view - strange behaviour:
"a b c".split(maxsplit=1)
TypeError: split() takes no keyword arguments
Why does str.split() not take keyword arguments, even though it would make sense? I found this behavior both in Python2 and Python3.
See this bug and its superseder.
str.split() is a native function in CPython, and as such exhibits the behavior described here:
CPython implementation detail: An implementation may provide built-in
functions whose positional parameters do not have names, even if they
are ‘named’ for the purpose of documentation, and which therefore
cannot be supplied by keyword. In CPython, this is the case for
functions implemented in C that use PyArg_ParseTuple() to parse their
arguments.
str.split is a builtin method implemented in C. Unfortunately some builtin functions/methods do not accept keyword arguments. See this bug report.

Why does range() not take keyword arguments? [duplicate]

In the python documenation, it says:
Any function argument, no matter non-optional or optional (with default value) can be called as keyword argument as long as one of the argument names matches. Keyword argument, however, must follow all positional arguments.
I tried this out:
kwargs = {'step':-1, 'start':10, 'stop':5}
list(range(**kwargs))
But python gives men an error:
TypeError: range() takes no keyword arguments
Why is this?
range() is not a python function. It is a C type; C types follow different rules for arguments and range() only accepts positional arguments.
See the Calls expressions documentation:
CPython implementation detail: An implementation may provide built-in functions whose positional parameters do not have names, even if they are ‘named’ for the purpose of documentation, and which therefore cannot be supplied by keyword. In CPython, this is the case for functions implemented in C that use PyArg_ParseTuple() to parse their arguments.
The positional parameters of range() are not named so cannot be used as keyword arguments.
As you know by now the real answer is that range is a C function which for some reason does not have the same rules of python (would be nice to know why).
But you can do this instead:
range(*{'start':0,'stop':10,'step':2}.values())

What does the / sign means in the signature of numpy functions like sqrt? [duplicate]

What does the / mean in Python 3.4's help output for range before the closing parenthesis?
>>> help(range)
Help on class range in module builtins:
class range(object)
| range(stop) -> range object
| range(start, stop[, step]) -> range object
|
| Return a virtual sequence of numbers from start to stop by step.
|
| Methods defined here:
|
| __contains__(self, key, /)
| Return key in self.
|
| __eq__(self, value, /)
| Return self==value.
...
It signifies the end of the positional only parameters, parameters you cannot use as keyword parameters. Before Python 3.8, such parameters could only be specified in the C API.
It means the key argument to __contains__ can only be passed in by position (range(5).__contains__(3)), not as a keyword argument (range(5).__contains__(key=3)), something you can do with positional arguments in pure-python functions.
Also see the Argument Clinic documentation:
To mark all parameters as positional-only in Argument Clinic, add a / on a line by itself after the last parameter, indented the same as the parameter lines.
and the (very recent addition to) the Python FAQ:
A slash in the argument list of a function denotes that the parameters prior to it are positional-only. Positional-only parameters are the ones without an externally-usable name. Upon calling a function that accepts positional-only parameters, arguments are mapped to parameters based solely on their position.
The syntax is now part of the Python language specification, as of version 3.8, see PEP 570 – Python Positional-Only Parameters. Before PEP 570, the syntax was already reserved for possible future inclusion in Python, see PEP 457 - Syntax For Positional-Only Parameters.
Positional-only parameters can lead to cleaner and clearer APIs, make pure-Python implementations of otherwise C-only modules more consistent and easier to maintain, and because positional-only parameters require very little processing, they lead to faster Python code.
I asked this question myself. :) Found out that / was originally proposed by Guido in here.
Alternative proposal: how about using '/' ? It's kind of the opposite
of '*' which means "keyword argument", and '/' is not a new character.
Then his proposal won.
Heh. If that's true, my '/' proposal wins:
def foo(pos_only, /, pos_or_kw, *, kw_only): ...
I think the very relevant document covering this is PEP 570.
Where recap section looks nice.
Recap
The use case will determine which parameters to use in the function definition:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
As guidance:
Use positional-only if names do not matter or have no meaning, and there are only a few arguments which will always be passed in the same order.
Use keyword-only when names have meaning and the function definition is more understandable by being explicit with names.
If the function ends with /
def foo(p1, p2, /)
This means all functional arguments are positional.
Forward Slash (/) indicates all arguments prior to it are positional only argument. Positional only arguments feature was added in python 3.8 after PEP 570 was accepted. Initially this notation was defined in PEP 457 - Notation for Notation For Positional-Only Parameters
Parameters in function definition prior Foraward slash (/) are positional only and parameters followed by slash(/) can be of any kind as per syntax. Where arguments are mapped to positional only parameters solely based on their position upon calling a function. Passing positional-only parameters by keywords(name) is invalid.
Let's take following example
def foo(a, b, / , x, y):
print("positional ", a, b)
print("positional or keyword", x, y)
Here in the above function definition parameters a and b are positional-only, while x or y can be either positional or keyword.
Following function calls are valid
foo(40, 20, 99, 39)
foo(40, 3.14, "hello", y="world")
foo(1.45, 3.14, x="hello", y="world")
But, following function call is not valid which raises an exception TypeError since a, b are not passed as positional arguments instead passed as keyword
foo(a=1.45, b=3.14, x=1, y=4)
TypeError: foo() got some positional-only arguments passed as keyword
arguments: 'a, b'
Many built in function in python accept positional only arguments where passing arguments by keyword doesn't make sense. For example built-in function len accepts only one positional(only) argument, Where calling len as len(obj="hello world") impairs readability, check help(len).
>>> help(len)
Help on built-in function len in module builtins:
len(obj, /)
Return the number of items in a container.
Positional only parameters make underlying c/library functions easy to maintain. It allows parameters names of positional only parameters to be changes in future without risk of breaking client code that uses API
Last but not least, positional only parameters allow us to use their names to be used in variable length keyword arguments. Check following example
>>> def f(a, b, /, **kwargs):
... print(a, b, kwargs)
...
>>> f(10, 20, a=1, b=2, c=3) # a and b are used in two ways
10 20 {'a': 1, 'b': 2, 'c': 3}
Positional-only parameters syntax was officially added to python3.8. Checkout what's new python3.8 - positional only arguments
PEP Related: PEP 570 -- Python Positional-Only Parameters

Why does str.split not take keyword arguments?

I came across this - in my view - strange behaviour:
"a b c".split(maxsplit=1)
TypeError: split() takes no keyword arguments
Why does str.split() not take keyword arguments, even though it would make sense? I found this behavior both in Python2 and Python3.
See this bug and its superseder.
str.split() is a native function in CPython, and as such exhibits the behavior described here:
CPython implementation detail: An implementation may provide built-in
functions whose positional parameters do not have names, even if they
are ‘named’ for the purpose of documentation, and which therefore
cannot be supplied by keyword. In CPython, this is the case for
functions implemented in C that use PyArg_ParseTuple() to parse their
arguments.
str.split is a builtin method implemented in C. Unfortunately some builtin functions/methods do not accept keyword arguments. See this bug report.

Categories

Resources