In [1]:
name = '2017-01-20-function-quirks'
title = 'Some peculiarities of using functions in Python'
tags = 'basics'
author = 'Denis Sergeev'
In [2]:
from nb_tools import connect_notebook_to_post
from IPython.core.display import HTML, Image
html = connect_notebook_to_post(name, title, tags, author)
In [3]:
def my_super_function():
pass
In [4]:
def even_better():
print('This is executed within a function')
In [5]:
even_better()
In [6]:
type(even_better)
Out[6]:
In [7]:
import numpy as np
In [8]:
def uv2wdir(u, v):
"""Calculate horizontal wind direction (meteorological notation)"""
return 180 + 180 / np.pi * np.arctan2(u, v)
In [9]:
a = uv2wdir(10, -10)
a
Out[9]:
In [10]:
type(a)
Out[10]:
In [11]:
def myfun(list_of_strings, separator=' ', another=123):
result = separator.join(list_of_strings)
return result
In [12]:
words = ['This', 'is', 'my', 'Function']
In [13]:
myfun(words, another=456, separator='-------')
Out[13]:
In [14]:
default_number = 10
In [15]:
def double_it(x=default_number):
return x * 2
In [16]:
double_it()
Out[16]:
In [17]:
double_it(2)
Out[17]:
In [18]:
default_number = 100000000
In [19]:
double_it()
Out[19]:
But what if we used a mutable type as a default argument?
In [20]:
def add_items_bad(element, times=1, lst=[]):
for _ in range(times):
lst.append(element)
return lst
In [21]:
mylist = add_items_bad('a', 3)
print(mylist)
In [22]:
another_list = add_items_bad('b', 5)
print(another_list)
In [23]:
def add_items_good(element, times=1, lst=None):
if lst is None:
lst = []
for _ in range(times):
lst.append(element)
return lst
In [24]:
mylist = add_items_good('a', 3)
print(mylist)
In [25]:
another_list = add_items_good('b', 5)
print(another_list)
Variables declared outside the function can be referenced within the function:
In [26]:
x = 5
In [27]:
def add_x(y):
return x + y
In [28]:
add_x(20)
Out[28]:
But these global variables cannot be modified within the function, unless declared global in the function.
In [29]:
def setx(y):
global x
x = y
print('x is {}'.format(x))
In [30]:
x
Out[30]:
In [31]:
setx(10)
In [32]:
print(x)
In [33]:
def foo():
a = 1
print(locals())
In [34]:
foo()
Special forms of parameters:
*args: any number of positional arguments packed into a tuple**kwargs: any number of keyword arguments packed into a dictionary
In [35]:
def variable_args(*args, **kwargs):
print('args are', args)
print('kwargs are', kwargs)
if 'z' in kwargs:
print(kwargs['z'])
In [36]:
variable_args('foo', 'bar', x=1, y=2)
In [37]:
def smallest(x, y):
if x < y:
return x
else:
return y
In [38]:
smallest(1, 2)
Out[38]:
In [39]:
# smallest(1, 2, 3) <- results in TypeError
In [40]:
def smallest(x, *args):
small = x
for y in args:
if y < small:
small= y
return small
In [41]:
smallest(11)
Out[41]:
Unpacking a dictionary of keyword arguments is particularly handy in matplotlib.
In [42]:
import matplotlib.pyplot as plt
In [43]:
%matplotlib inline
In [44]:
arr1 = np.random.rand(100)
arr2 = np.random.rand(100)
In [45]:
style1 = dict(linewidth=3, color='#FF0123')
style2 = dict(linestyle='--', color='skyblue')
In [46]:
plt.plot(arr1, **style1)
plt.plot(arr2, **style2)
Out[46]:
Functions are first-class objects. This means that functions can be passed around, and used as arguments, just like any other value (e.g, string, int, float).
In [47]:
def find_special_numbers(special_selector, limit=10):
found = []
n = 0
while len(found) < limit:
if special_selector(n):
found.append(n)
n += 1
return found
In [48]:
def check_odd(a):
return a % 2 == 1
In [49]:
mylist = find_special_numbers(check_odd, 25)
In [50]:
for n in mylist:
print(n, end=',')
But lots of small functions can clutter your code...
Highly pythonic!
check = i -> return True if i % 6 == 0
In [51]:
check = lambda i: i % 6 == 0
In [52]:
#check = lambda
Lambdas usually are not defined on their own, but inserted in-place.
In [53]:
find_special_numbers(lambda i: i % 6 == 0, 5)
Out[53]:
In [54]:
lyric = "Never gonna give you up"
In [55]:
words = lyric.split()
words
Out[55]:
In [56]:
sorted(words, key=lambda x: x.lower())
Out[56]:
Just using sorted() gives us not what we want:
In [57]:
lst = ['20', '1', '2', '100']
In [58]:
sorted(lst)
Out[58]:
But we can use a lambda-expression to overcome this problem:
Option 1:
In [59]:
sorted(lst, key=lambda x: int(x))
Out[59]:
Option 2:
In [60]:
sorted(lst, key=lambda x: x.zfill(16))
Out[60]:
By the way, what does zfill() method do? It pads a string with zeros:
In [61]:
'aaaa'.zfill(10)
Out[61]:
In [62]:
HTML(html)
Out[62]: