Flexible calls
Look at how you call print: print("a"), print("a", "b", "c"), print("a", end=""), print("a", "b", sep="-"). One function accepts any number of values plus optional settings by name. Python gives your own functions the same powers, and using them well is a big part of writing Pythonic code.
Today: positional and keyword arguments, default values (and the most famous Python trap), collecting extra arguments with *args and kwargs, how Python finds a name (scope), and small anonymous functions with lambda.
Arguments by position and by name
Every function is defined with def name(parameters): and an indented body; return sends a value back, and a function that never returns gives back None. Callers can pass arguments by position, price(180, 2), or by name, price(each=180, qty=2). Keyword arguments make calls readable and order-independent.
A parameter with = has a default argument used when the caller leaves it out. A parameter written *args collects any extra positional arguments into a tuple, and kwargs collects extra keyword arguments into a dict. Parameters after *args (or after a bare *) are keyword-only arguments: they can only be passed by name, which stops a caller passing a discount by accident.
def total(*prices, discount=0):
return sum(prices) * (100 - discount) / 100
def tag(name, **attrs):
return name, attrs
print(total(100, 50), total(100, 50, discount=10))
print(tag("img", src="momo.png", alt="momo"))The stars also work the other way, when *calling*: f(*items) spreads a list into positional arguments and f(options) spreads a dict into keyword arguments.