I 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.