Consider a function with signature f(a, b)
. In future, I would like to change the signature to f(a, *, b)
, disallowing b to be passed as positional argument.
To reduce the impact of the change, I want to first deprecate specifying b positionally, warning users that do so.
For that I would like to write something like:
def f(a, b):
frame = inspect.currentframe()
if b in frame.specified_as_positional:
print('Do not do that')
else:
print('Good')
The result would be that
>>> f(1, 2)
'Do not do that'
>>> f(1, b=2)
'Good'
inspect.getargvalues(frame)
does not seem to be sufficient. The ArgInfo object just provides
>>> f(1,b=2)
ArgInfo(args=['a', 'b'], varargs=None, keywords=None, locals={'a': 1, 'b': 2})
Is such inspection even possible in Python? Conceptually the interpreter does not seem to be required to remember if a argument was specified positionally or as keyword.
Python 2 support would be nice to have but is not strictly required.
Python functions can contain two types of arguments: positional arguments and keyword arguments. Positional arguments must be included in the correct order. Keyword arguments are included with a keyword and equals sign.
Keyword arguments (or named arguments) are values that, when passed into a function, are identifiable by specific parameter names. A keyword argument is preceded by a parameter and the assignment operator, = . Keyword arguments can be likened to dictionaries in that they map a value to a keyword.
When you use keyword arguments in a function call, the caller identifies the arguments by the parameter name. This allows you to skip arguments or place them out of order because the Python interpreter is able to use the keywords provided to match the values with parameters.
Embrace keyword arguments in PythonConsider using the * operator to require those arguments be specified as keyword arguments. And remember that you can accept arbitrary keyword arguments to the functions you define and pass arbitrary keyword arguments to the functions you call by using the ** operator.
You can use a wrapper to add an extra step between the user and the function. In that step, you can examine the arguments before the names matter. Note that this depends on the fact that b
doesn't have a default value and always must be given as an arg.
functools.wraps
is used to make the decorated function resemble the original in a bunch of ways.
import functools
import warnings
def deprecate_positional(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if 'b' not in kwargs:
warnings.warn(
'b will soon be keyword-only',
DeprecationWarning,
stacklevel=2
)
return fun(*args, **kwargs)
return wrapper
@deprecate_positional
def f(a, b):
return a + b
>>> f(1, b=2)
3
>>> f(1, 2)
Warning (from warnings module):
File "C:/Users/nwerth/Desktop/deprecate_positional.py", line 36
print(f(1, 2))
DeprecationWarning: b will soon be keyword-only
3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With