5 ms·
The thing that I love about python is keyword arguments. It makes the code so much more readable. The wildcards, while powerful, reduce readability. For me, a
by kghose 13y ago
The thing that I love about python is keyword arguments. It makes the code so much more readable. The wildcards, while powerful, reduce readability.
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.
- nostrademons 13y agoI find the wildcards most useful when implementing decorators or other cross-cutting metaprogramming features. For example, you could easily do a tracing decorator like this: 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.
- lclarkmichalek 13y agoYou can use the `functools.wraps` decorator to deal with copying all (most) metadata from the wrapped function: 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.wraps http://docs.python.org/3/library/functools.html#functools.wr...
- darth_aardvark 13y agoI've been looking for something exactly like this. It's so tedious trying to remember what metadata needs to be copied from a function when making a decorator. Thanks!
- nostrademons 13y agoAlso, BTW, I hate the practice of using default arguments to avoid breaking existing code, because it makes the code reflect how it got there and not what it does. You end up with a bunch of call sites, some that take n arguments and some that take n+1, and you have to look at the function definition to see what the n-arg call sites are doing. I would much rather use an automated refactoring tool to edit all call sites to take n+1 arguments with the default inline in the call, although this is a bit hard to do in Python. 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.
- tome 13y agoYou can also just give the n+1 argument function a different name, and implement the old version in terms of it.
- ngoldbaum 13y agoI'm no expert, but I've found it's best to use kwargs near the middle of a class hierarchy. This makes it much simpler to build the internal API - the classes near the top of the hierarchy have specific kwargs, but their subclasses don't need to explicitly enumerate them. One can then write user-facing subclasses at the bottom of the hierarchy that have explicit arguments and keywords arguments.