The syntax is the * and **. The names *args and **kwargs are only by convention but there's no need not to use them.
You would use *args when you're not sure how many arguments might be passed to your function, i.e. it allows you pass an arbitrary number of arguments to your function. For example:
>>> def print_everything(*args):
... count = 1
... for thing in args:
... print "%d. %s" % (count,thing)
... count += 1
...
>>> print_everything('apple', 'banana', 'cabbage')
1. apple
2. banana
3. cabbage
Similarly, **kwargs allows you to handle named arguments that you have not defined in advance:
>>> def table_things(**kwargs):
... for name,value in kwargs.items():
... print name, "=", value
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit
You can use these along with named arguments too. The explicit arguments get values first and then everything else is passed to *args and *kwargs. The named arguments come first in the list. For example:
def table_things(titlestring, **kwargs)
You can also use both in the same function definition but *args must occur before **kwargs.
You can also use the * and ** syntax when calling a function. For example:
>>> def print_three_things(a, b, c):
... print "a =", a, "& b =", b, "& c =", c
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark & b = baboon & c = cat
As you can see in this case it takes the list (or tuple) of items and matches them to the arguments in the function. Of course, you could have a * both in the function definition and in the function call.