In Python you may have a function definition:
def info(object, spacing=10, collapse=1)
which could be called in any of the following ways:<
Update:
I realized that using **kwargs
would not solve the problem. If your programmers change function arguments as they wish, one could, for example, change the function to this:
def info(foo, **kwargs):
and the old code would break again (because now every function call has to include the first argument).
It really comes down to what Bryan says.
(...) people might be adding parameters between
spacing
andcollapse
(...)
In general, when changing functions, new arguments should always go to the end. Otherwise it breaks the code. Should be obvious.
If someone changes the function so that the code breaks, this change has to be rejected.
(As Bryan says, it is like a contract)
(...) sometimes it's not always clear as to what needs to go in.
By looking at the signature of the function (i.e def info(object, spacing=10, collapse=1)
) one should immediately see that every argument that has not a default value, is mandatory.
What the argument is for, should go into the docstring.
Old answer (kept for completeness):
This is probably not a good solution:
You can define functions this way:
def info(**kwargs):
''' Some docstring here describing possible and mandatory arguments. '''
spacing = kwargs.get('spacing', 15)
obj = kwargs.get('object', None)
if not obj:
raise ValueError('object is needed')
kwargs
is a dictionary that contains any keyword argument. You can check whether a mandatory argument is present and if not, raise an exception.
The downside is, that it might not be that obvious anymore, which arguments are possible, but with a proper docstring, it should be fine.
I don't get why a programmer will add a parameter in between two others in the first place.
If you want the function parameters to be used with names (e.g. info(spacing=15, object=odbchelper)
) then it shouldn't matter what order they are defined in, so you might as well put the new parameters at the end.
If you do want the order to matter then can't expect anything to work if you change it!
In Python 3 - Yes, you can specify *
in the argument list.
From docs:
Parameters after “*” or “*identifier” are keyword-only parameters and may only be passed used keyword arguments.
>>> def foo(pos, *, forcenamed):
... print(pos, forcenamed)
...
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)
This can also be combined with **kwargs
:
def foo(pos, *, forcenamed, **kwargs):
True, most programming languages make parameter order part of the function call contract, but this doesn't need to be so. Why would it? My understanding of the question is, then, if Python is any different to other programming languages in this respect. In addition to other good answers for Python 2, please consider the following:
__named_only_start = object()
def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
if _p is not __named_only_start:
raise TypeError("info() takes at most 3 positional arguments")
return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)
The only way a caller would be able to provide arguments spacing
and collapse
positionally (without an exception) would be:
info(arg1, arg2, arg3, module.__named_only_start, 11, 2)
The convention of not using private elements belonging to other modules already is very basic in Python. As with Python itself, this convention for parameters would only be semi-enforced.
Otherwise, calls would need to be of the form:
info(arg1, arg2, arg3, spacing=11, collapse=2)
A call
info(arg1, arg2, arg3, 11, 2)
would assign value 11 to parameter _p
and an exception risen by the function's first instruction.
Characteristics:
_p=__named_only_start
are admitted positionally (or by name)._p=__named_only_start
must be provided by name only (unless knowledge about the special sentinel object __named_only_start
is obtained and used).Pros:
__named_only_start
in the corresponding position.Cons:
__named_only_start
in the right position. Yes, this can also be seen as a pro.Please do keep in mind that this answer is only valid for Python 2. Python 3 implements the similar, but very elegant, language-supported mechanism described in other answers.
I've found that when I open my mind and think about it, no question or other's decision seems stupid, dumb, or just silly. Quite on the contrary: I typically learn a lot.
You can do that in a way that works in both Python 2 and Python 3, by making a "bogus" first keyword argument with a default value that will not occur "naturally". That keyword argument can be preceded by one or more arguments without value:
_dummy = object()
def info(object, _kw=_dummy, spacing=10, collapse=1):
if _kw is not _dummy:
raise TypeError("info() takes 1 positional argument but at least 2 were given")
This will allow:
info(odbchelper)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)
but not:
info(odbchelper, 12)
If you change the function to:
def info(_kw=_dummy, spacing=10, collapse=1):
then all arguments must have keywords and info(odbchelper)
will no longer work.
This will allow you to position additional keyword arguments any place after _kw
, without forcing you to put them after the last entry. This often makes sense, e.g. grouping thing logically or arranging keywords alphabetically can help with maintenance and development.
So there is no need to revert to using def(**kwargs)
and losing the signature information in your smart editor. Your social contract is to provide certain information, by forcing (some of them) to require keywords, the order these are presented in, has become irrelevant.