Python For Quantum Mechanics#
Week 3: Functions and Lambda Functions#
from IPython.display import YouTubeVideo
YouTubeVideo('h-TmiynfpCs',width=700, height=400)
Functions are useful to compactify code. Rather than writing 20 lines of code to do a specific task with certain values for the variables used, instead we define a function. The function performs the task taking in arbitrary arguments for the variables used. Here’s how we do it:
How To Write A Function And Return Values#
Notice the use of def
, the name of the function modulus
, the parentheses ()
which contain the arguments, and the colon :
.
The string we have inside the function is called a docstring, and is used to describe what the function does and what arguments it takes in. It is not necessary, but is good practice.
def modulus(x,y):
'''Docstring: This prints the modulus of two numbers \nhello hi right'''
print((x**2 + y**2)**0.5)
Now we call the function using it’s name, followed by inputting the arguments.
modulus(3,4)
print(type(modulus))
5.0
<class 'function'>
If a function gives some value we would like to use, rather than just printing a result, we use the return
statement as follows
def modulus(x,y):
'''Docstring: This returns the modulus of two numbers'''
return((x**2 + y**2)**0.5)
We usually assign the returned value to a variable like so
Mod = modulus(1,1)
Mod = Mod*3
print(Mod)
#Or you can treat the returned value like you would it's corresponding data type
print(3*modulus(1,1))#Here the returned value is not assigned to a variable but can still be acted upon by functions
4.242640687119286
4.242640687119286
We can also see the docstring of a function by using the help() function
help(modulus)
Help on function modulus in module __main__:
modulus(x, y)
Docstring: This returns the modulus of two numbers
Returning Multiple Values#
L = [0,1,2,3,4,5,6,7,8,9,10]
def describe_list(List):
'''This function takes in a list and returns values that describe said list'''
len_value = len(List)
max_value = max(List)
min_value = min(List)
sum_value = sum(List)
return len_value, max_value, min_value, sum_value
values = describe_list(L)
print(values)
print(type(values))
(11, 10, 0, 55)
<class 'tuple'>
As we see, returning multiple values gives us a tuple of those values. Just like we assigned a tuple to multiple variables before, we can do the same when calling this function
len_L,max_L,min_L,sum_L = describe_list(L)
print(len_L)
print(type(len_L))
print(sum_L)
print(type(sum_L))
11
<class 'int'>
55
<class 'int'>
No Arguments#
A function doesn’t actually have to take in arguments, for example:
def PrintingPress():
print("I am a futuristic printing press")
We call it without inserting arguments
PrintingPress()
I am a futuristic printing press
Implicit/Default Arguments#
Often times, some arguments carry the same value and on the rare occasion require change. We can take advantage of this using implicit arguments, written as follows
def modulus3d(x,y,z=0):
'''Docstring: This returns the modulus of three numbers'''
return((x**2 + y**2 + z**2)**0.5)
We set the argument to a commonly used value in the parantheses. We can now call this function without specifying the third argument “z”.
Mod = modulus3d(1,1)
print(Mod)
1.4142135623730951
If we want to change the value of this implicit argument, we simply specify it when we call the function.
Mod = modulus3d(1,1,1)
print(Mod)
1.7320508075688772
When using default arguments, you must be wary of using mutable objects like lists. Lets illustrate this in the following example.
def append_if(x,L=[]):
if x<10:
L.append(x)
return L
print(append_if(6))
print(append_if(3))
print(append_if(12))
print(append_if(4,[]))
print(append_if(7))
[6]
[6, 3]
[6, 3]
[4]
[6, 3, 7]
By not specifying the second argument, the same list will appear in future calls of the function except when we actually specify the argument. This can be a problem, in the definition of the function we want the list to be empty by default, but this is ruined for future calling. Let’s see how we can fix this
def append_if(x,L=None):
if not L:
L = []
if x<10:
L.append(x)
return L
print(append_if(6))
print(append_if(3))
print(append_if(12))
print(append_if(4,[]))
print(append_if(7))
[6]
[3]
[]
[4]
[7]
Keyword Arguments#
It is not necessary to enter the arguments in the correct order when calling a function, so long as we assign them to the correct variable. For example
def xyz_expression(x,y,z):
return 2*x**2 - y + z/2.
print(xyz_expression(y=1.,z=-8.,x=3.))
13.0
Unknown Amount Of Arguments, *args
#
You may need to define a function in which want to input varying numbers of arguments. To do this, we precede the argumentsw with *
For example
#Here args represent the individual cost of various items purchased
def grocery_change(money=100,*args):
for i in args:
money = money - i
return money
print(grocery_change(100,20,40,35))
print(grocery_change(100,30,40,3,9,14))
print(grocery_change())
print(grocery_change(20,10))#Notice here, money is set to 20
5
4
100
10
**kwargs
#
This is similar to *args
except it is used for an unknown amount of keyword arguments or the inputting of a dictionary. Recall, items()
is a dictionary method used to display a dictionary’s key, value pairs.
def kw_demo(**kwargs):
for key, value in kwargs.items():
print("key: {}, value: {}".format(key,value))
kw_demo(Name="Conor",Age="24",Occupation="Quantum Computational Scientist")
key: Name, value: Conor
key: Age, value: 24
key: Occupation, value: Quantum Computational Scientist
Job_Details = {"Name":"Conor","Age":24,"Occupation":"Quantum Computational Scientist"}
kw_demo(**Job_Details)#Notice the use of ** for the dictionary
key: Name, value: Conor
key: Age, value: 24
key: Occupation, value: Quantum Computational Scientist
Global And Local Variables#
Global variables are defined outside of functions. Local Variables are defined inside functions. We can use gloabl variables both outside and inside a function. However local variable can only be used inside a function. For example
def subtractor(a):
b = 4
return a-b
a=10
print(subtractor(a))
print(b)
6
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[21], line 7
5 a=10
6 print(subtractor(a))
----> 7 print(b)
NameError: name 'b' is not defined
Here a
is a global variable and b
is a local variable. When we attempt to use b
outside of the function we get en error. However, we can define b
as global inside the function by making the declaration global b
, in the following way
def subtractor(a):
global b
b = 4
return a-b
a=10
print(subtractor(a))
print(b)
6
4
Lambda Functions#
Lambda functions are compactified, unnamed functions that are very commonly used in cojunction with lists.
y = lambda x:x**2 + x +2
We call it as follows
print(y(4))
22
The map()
Function#
This map()
function is useful for manipulating lists. It inputs each element in the list into a lambda function.
L = [1,2,3,4,5,6,7,8,9,10]
M = map(lambda x:x**2,L)
print(type(M))
M = list(M) #Cast as a list
print(M)
<class 'map'>
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Observe what happens when we use an inequality.
print(M)
M = map(lambda x:x<=50,M)
print(type(M))
M = list(M)
print(M)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
<class 'map'>
[True, True, True, True, True, True, True, False, False, False]
We can also also add more arguments, or lists, allowing us to perform various operations on multiple lists.
print(L)
print(M)
S = map(lambda x,y:x*y,L,M)
S = list(S)
print(S)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[True, True, True, True, True, True, True, False, False, False]
[1, 2, 3, 4, 5, 6, 7, 0, 0, 0]
We just multiplied elements of a list together. We can re-interpret lists as matrices and perform fundamental matrix operations using lambda functions. We will later see that the Numpy package greatly simplifies this by giving us a library to manipulate lists/arrays/matrices.
The map()
function is not only constrained to using lambda functions. We can also use other built-in functions.
Float_List = map(float,S)
Float_List = list(Float_List)
print(Float_List)
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0]
We have converted each element in our list to a float.
The filter()
Function#
The filter()
function is useful for eliminating particular elements from lists.
M = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
print(M)
M = filter(lambda x:x<=50,M)
print(type(M))
M = list(M)
print(M)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
<class 'filter'>
[1, 4, 9, 16, 25, 36, 49]
This is the same as using inequalities with the map()
above, except the true values are returned instead.