To create a function in Python, you use the def
keyword, followed by the function name and argument list, and a
colon to begin the function body:
# a_function.py
def a_function(response):
val = 0
if response == "yes":
print("affirmative")
val = 1
print("continuing...")
return val
print(a_function("no"))
print(a_function("yes"))Notice there is no type information in the function signature: all it specifies is the name of the function and the argument identifiers, but no argument types or return types. Python is dynamically typed, which means it enforces type constraints at runtime. For example, different types can be both passed to and returned from the same function:
# different_returns.py
def different_returns(arg):
if arg == 1:
return "one"
if arg == "one":
return True
print(different_returns(1))
print(different_returns("one"))The only constraints on an object that is passed into the
function are that the function can apply its operations to that
object, but other than that, it doesn’t care. Here, the same
function applies the ‘+’ operator to integers and
strings:
# sum.py
def sum(arg1, arg2):
return arg1 + arg2
print(sum(42, 47))
print(sum('spam ', "eggs"))Parameters can have defaults, and callers can pass arguments by name in any order. Keyword arguments make a call self-documenting:
# default_args.py
def connect(host, port=5432, timeout=30):
return f"{host}:{port} (timeout {timeout}s)"
print(connect("db.example.com")) # uses both defaults
print(connect("db.example.com", timeout=5)) # skip to a keyword
print(connect(port=80, host="web.example.com")) # any order by nameA default value is evaluated once, when the function is defined, not on each call. A mutable default is therefore shared across calls:
# mutable_default.py
def bad_append(item, target=[]): # the same list every call
target.append(item)
return target
print(bad_append(1)) # [1]
print(bad_append(2)) # [1, 2]: surprise, the default kept the 1
def good_append(item, target=None):
if target is None:
target = [] # a fresh list each call
target.append(item)
return target
print(good_append(1)) # [1]
print(good_append(2)) # [2]Thus a mutable default persists between calls: it is created once, at definition time, and lives on the function, not recreated on each call. This behavior is a common confusion for newcomers to the language.
A *args parameter collects extra positional
arguments into a tuple, and **kwargs collects extra
keyword arguments into a dictionary:
# var_args.py
def report(label, *values, **options):
print(label, values, options)
report("nums", 1, 2, 3) # nums (1, 2, 3) {}
report("point", 3, 4, color="red", size=10) # extras land in optionsThe same * and ** unpack a
sequence or dictionary back into arguments at a call site, the
mirror image of collecting them.
Two markers in a parameter list control how callers may pass
arguments. A / ends the positional-only
parameters: every parameter before it must be passed by position,
never by name. A * begins the keyword-only
parameters: every parameter after it must be passed by name.
# param_markers.py
# `/` ends the positional-only parameters.
# `*` begins the keyword-only parameters.
def divide(a, b, /):
return a / b
print(divide(10, 2)) # 5.0
def make_user(name, *, admin=False):
return f"{name} (admin={admin})"
print(make_user("Bob")) # Bob (admin=False)
print(make_user("Sue", admin=True)) # Sue (admin=True)Calling divide(a=10, b=2) is an error, because
a and b are positional-only. Calling
make_user("Sue", True) is an error, because
admin is keyword-only.
You meet / throughout the standard library, where
many built-in functions take positional-only arguments, such as
dict.get(key, default, /). Marking a parameter
positional-only also keeps its name out of the method’s contract.
That matters when a subclass overrides a method: since the name is
not part of the interface, the subclass can rename the parameter,
and a type checker will not object.
A lambda is a small anonymous function written as a
single expression. It is handy for passing behavior to functions
like sorted():
# lambdas.py
words = ["banana", "kiwi", "apple", "fig"]
print(sorted(words, key=lambda w: len(w))) # sort by length
square = lambda n: n * n # usually prefer def
print(square(9)) # 81Python’s lambdas are rather constrained because they comprise a single expression. For anything more complicated you are expected to write a separate function.
* also works in the other direction: at a call site
it unpacks a sequence into separate positional
arguments.
# unpacking.py
# Turn a sequence into positional arguments with *.
def f(a: int, b: int, c: int) -> None:
print(a, b, c)
x = [1, 2, 3]
f(*x)
f(*(1, 2, 3))