Python - Functions
Functions allow you to define a block of code that can be called over and over again. They can be passed parameters. They also return a value. If no return value is specified they will return avalue of None.
- Functions return None by default
Functions can be defined like this.
def test1():
x + 5 + 4
print(x)
You can use parameters like this:
def test1(x):
x += 5
print(x)
Call a fuction like this:
test1(4)
You can assign a function to a variable and use that as an alias or alternate name for the same function:
t = test1
t(3)
Here is an example using a return value:
def test1(x):
x += 5
return x
Using return without a param or ommitting return will result in None being returned. Here is an example that eturns none:
def test1(x):
pass
return
You can specify default argument values for parameters:
def test2(name, count=5, description='A silly value.'):
pass
test2('Steve')
test2('y', 7)
test2('y', 2, "A serious description.")
- The arg without a default is required.
- Args with defaults are optional ( must be after args without default values ).
If you use a variable for a default value it will be evaluated at the time of fuction definition.
i = 9
def test2(x=i):
print(x)
i = 4
test2() # still prints 9
Default values set in a function definition are evaluated only once. For example, values are added to L every time this is called:
def test1(v, a=[]):
a.append(v)
return a
You can avoid this by using a new list every time:
def test1(v, a=None):
if a is None:
a = []
a.append(v)
return a
Keyword Arguments
Python functions can use keyword arguments in addtion to positional arguments.
def load_data(count, status='a stiff', format='voom', color='Norwegian Blue'):
print("Format: ", format)
print("Count: ", count)
print("Color: ", color)
print("Status: ", status)
Valid:
load_data(123) # 1 positional arg
load_data(count=123) # 1 keyword arg
load_data(count=123, format='compact') # 2 keyword arg
load_data(format='compact', count=123) # 2 keyword arg
load_data(123, 'frozen', 'compact') # 3 positional arg
load_data(123, status='melted') # 1 positional, 1 keyword
load_data(123, status='melted') # 1 positional, 1 keyword
Not valid:
load_data() # missing non-optional argument
load_data(format="a", 5) # positional argument after keyword argument
load_data(123, count=456) # duplicate aregument specified
load_data(category='mechanical') # undefined keyword argument
- Positional args need to come before keyword args
- Otherwise keyword args can be out of order
- Args without a default can still be used as a keyword arg
Arbitrary Argument Lists
**name | will hold a dictionary with all keyword args that aren’t formal args |
*name | tuple containing all positional args that aren’t formal args |
def test3(x, *y, **z):
print("Value: ", x)
for i in y:
print(i)
for i in z:
print(i, ":", z[i])
test3("a", "b", "c", type="x", status="y", value="z")
If the name of an arg passed as a packed arg list conflicts with the name of a positional arg, this is totally fine and will work without ambiguity. This is a little bit strange and feels wrong but it isn’t. See this example below.
def test4(x, /, **kwds):
return 'x' in kwds
z = test4(2, **{'x': 3})
print(z) # will print 3 not 2
Positional only is good for when:
- you don’t want the calling user to have the names available
- the names don’t have any meaning
Unpacking Argument Lists
You can pass an argument list like this and it will be unpacked:
def test1( a, b, c ):
print(a, b, c)
x = ('x', 'y', 'z')
test1(*x)
You can also basically pass a dictionary of keyword arguments like this:
def test2( a, b, c ):
print(a, b, c)
x2 = {"a": "x", "b": "y", "c": "z"}
test2(**x2)
Unpack and pack - you can do this too:
def test3( *a ):
print(a)
test3(*x)
Lambda Expressions
- limited to a single expression
- basically just functions
- can be passed as a parameter
To create a lambda, specify any variables, then a colon, then an expression. The expression can include conditionals. What ever the expression returns is what the lambda will return.
Here are a few lambda examples.
a = lambda : print('test')
b = lambda a: a + 5
c = lambda a,b: a * b + 5
d = lambda a: "larger" if a > 5 else "smaller"
e = lambda a,b: b - 1 if a > 5 else b + 1
print(a())
print(b(3))
print(c(3,5))
print(d(2))
print(d(7))
print(e(1,2))
print(e(7,2))
You can create a function that dynamically generates another function as a lambda.
def test1(n):
return lambda x: x + n
x = test1(42)
x(0)
x(1)
Function Annotations
Function annotations are another interesting feature. You can use these to do the following:
- types for each argument
- type for return value
Here is an example:
def test3(a: str, b: str = 'xyz') -> str:
print("Annotations: ", test3.__annotations__)
print("Args:", a, b)
return a + ' ' + b
Documentation
You will generally specify documentation for a function as a doc string at the beginning of the function definition.
Documentation string example:
def test3():
"""Important function to process data.
This function exists to process data.
Here we cover the specifics and everything
that you need to know.
"""
pass
print(test3__doc__)