*args and **kwargs in python explained
freepythontips.wordpress.com
freepythontips.wordpress.com
If you are making functions that modify other functions (like decorators), these are highly useful. In Python you can't guarantee that all functions you will want to modify are going to have the same type signature, so you need to be able to gracefully handle all possible combinations and pass them on to your function body. Sometimes you'll just pass args and kwargs on to a wrapped function...other times you'll do things like pick out a single argument from kwargs and use it to do something useful. But make no mistake, in Python these come in handy to have!
process_payment_data(**kwargs)
In the docs and then have to go find out what possible values kwargs could take.For me, a equally powerful AND readable way is to have named arguments with defaults. So when you change the function definition to add more arguments you can insert defaults so as not to break existing code.
def trace(fn):
def worker(*args):
logging.info('Called %s with args %r', fn.__name__, args)
return fn(*args)
worker.__name__ = fn.__name__
# other decorator boilerplate
return worker
@trace
def foo(bar, baz):
...
@trace
def foo2(x1, x2, x3):
...
(Note that logging.info itself takes args & kwargs as its arguments, and passes them along as formatting arguments for the string.)If you've ever looked at AspectJ or even Haskell, this sort of functionality is virtually impossible in a statically-typed language. You go through all sorts of contortions to be able to say "I want to operate on any type of function, and I don't care how many or what type of arguments it has", and then come out with something insanely complicated that satisfies nobody. For certain use-cases, you really don't care what type of arguments it takes, you just want to pass it through to some other function.
I do wonder if there's some other language feature that could be used to achieve this without breaking documentation or static analysis, though.
def trace(fn):
@functools.wraps(fn)
def worker(*args):
logging.info('Called %s with args %r', fn.__name__, args)
return fn(*args)
return worker
Docs: http://docs.python.org/3/library/functools.html#functools.wr...In my experience, the best use for default arguments is when the API contract really does call for "default usage", but occasionally you need to pass an extra arg for special purposes. So if 95% of call sites only need n args but 1-2 of them need to vary a parameter or two of the algorithm, then defaults are appropriate. Passing the extra args by keyword usually ends up being much more readable in this case though.
The only downside I see to that is the ability to write code poorly by passing junk arguments into functions that never get used. But you can do that with any function that declares kwargs or args right now.
Or just reserve args and kwargs and have it so functions with them in their arguments list have the same behavior as args an kwargs (just without the ugly stars). Maybe even denote them as __args__ and __kwargs__ in the same way you access special member functions of objects through __foo__ syntax, that would at least (to me) be consistent. I did a cursory google search if there is any language wide syntax surrounding star + variable name, and I couldn't find any, which seems to mean the name was just a hack using the syntax of a pointer dereference from C.
Am I wrong here and just missing some grand logic behind it? The rest of the language is really sensible so I probably am.
* You wouldn't get TypeErrors for broken code. Most code doesn't use or need star args / kwargs, but if I understand correctly your first proposal, it'd mean no TypeErrors on function signatures, making it harder to find coding errors.
* Python has very few keywords on purpose, and I don't think they'd like adding the fairly generic, non-keyword-seeming names like "args" and "kwargs".
* "Explicit is better than implicit" is part of the Python philosophy. The single and double stars may "look ugly", but that's part of the point. They're not just ordinary variables, and they do something special, so they need to stand out.
* Your proposal of using __args__ is at least explicit in their use, but not in the function definition.
For what it's worth, I mostly use star args / kwargs in wrapper or "proxy" functions: functions that do something like logging and then call through to another function to do the main work.
>>> def foo(a, b="Default"):
... print(a, b)
...
>>> foo(1, 2)
(1, 2)
>>> foo(1)
(1, 'Default')
>>> foo(1, b="bar")
(1, 'bar')
>>> def baz(*args, **kwargs):
... print(args, kwargs)
...
>>> args = (1, 2, 3)
>>> kwargs = {'foo': "bar"}
>>> baz(*args, **kwargs)
((1, 2, 3), {'foo': 'bar'})
It's the first example that kind of seems 'off'. It's probably just cross-polination from my CL experience (which seems 'more correct'): ?> (defun foo (a &key (b "Default")) (list a b))
FOO
?> (foo 1 2)
Error: Incorrect keyword arguments
For the most part it hasn't been an issue when I'm writing the code. But when I read code that does this stuff on the call side frequently (or isn't consistent in its use over time) it can be a little frustrating. >>> def foo(a, *, b="Default"):
... print((a, b))
...
>>> foo(1, b="bar")
(1, 'bar')
>>> foo(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes 1 positional argument but 2 were givenPython 3 is definitely a step in the right direction. I wish adoption was a little more rapid.
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
etc.
There are a few implicit things in Python, but I would consider those the blemishes on the language.here you go - http://svn.python.org/view/python/trunk/Lib/collections.py?v... - line 32.
probably you need to pass in an OrderedDict instance.
There are easy ways around this, e.g. just passing something ordered to `f`, but the keyword argument syntax seems the most concise to me.
Or maybe I do not get you approach - in that case, could you give an example?
Now, if you had said the other way around (ppl expect them to be order-sensitive but they are order-insensitive), that would have made sense...
Conversely, If (because I think it is order-sensitive) I believe that, in order to make the function do what I want, I have to call f(a=1,b=2) and not f(b=2,a=1), then I'm not going to change the order willy-nilly, so I won't be affected by my expectations not matching reality.
I think they are useful for systems with big configurations (that grow with features during development). I give every customizable function a reference to the big master config dict. Still not sure if that's a good design or not ...