def sum_a_b_sq(a, b):
return a + b ** 2
Lesson 3
Overview
- Tricks for functions
- Lambda functions
*
and**
- Composing functions
- Default argument values
- Tricks for lists
- List comprehension
sorted()
zip()
any()
andall()
- Tricks for strings
lower()
split()
strip()
startswith()
Type casting
PA2 Review
1. Tricks for functions
1.1 Lambda functions
Shorthand functions for simple tasks. Especially useful for sorting (will discuss soon).
= lambda a, b: a + b ** 2
sum_a_b_sq print(sum_a_b_sq(2, 4))
18
1.2 *
and **
*
unpacks a list - especially useful for function arguments
**
unpacks a dictionary - especially useful for function arguments
Unpacking a list inside another list seems impossible at first
1, 2, [3, 4], 5] [
[1, 2, [3, 4], 5]
It is possible with *
1, 2, *[3, 4], 5] [
[1, 2, 3, 4, 5]
Unpacking a dictionary inside another dictionary seems impossible at first
'a': 1, 'b': 2, {'c': 3, 'd': 4}} {
SyntaxError: ':' expected after dictionary key (192525557.py, line 1)
It is possible with **
'a': 1, 'b': 2, **{'c': 3, 'd': 4}} {
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
1.3 Composing functions
Apply multiple functions in one line
= lambda a, b: (a ** 2, b ** 3)
a_sq_b_cubed print(sum_a_b_sq(*a_sq_b_cubed(4, 12)))
2986000
Without the *
, the two function outputs are treated as one argument (since they are combined into a tuple)
print(sum_a_b_sq(a_sq_b_cubed(4, 12)))
TypeError: <lambda>() missing 1 required positional argument: 'b'
4, 12) a_sq_b_cubed(
(16, 1728)
The *
notation works only inside function calls
*a_sq_b_cubed(4, 12)
SyntaxError: can't use starred expression here (2410165554.py, line 1)
1.4 Default argument values
def print_name(first_name, last_name, middle_name=''):
print(first_name, middle_name, last_name)
'Adam', 'Oppenheimer', 'Alexander')
print_name('Adam', 'Alexander', 'Oppenheimer') print_name(
Adam Alexander Oppenheimer
Adam Oppenheimer Alexander
='Adam', middle_name='Alexander', last_name='Oppenheimer') print_name(first_name
Adam Alexander Oppenheimer
We can use **
notation in functions
**{'first_name': 'Adam', 'middle_name': 'Alexander', 'last_name': 'Oppenheimer'}) print_name(
Adam Alexander Oppenheimer
= {'first_name': 'Adam', 'middle_name': 'Alexander', 'last_name': 'Oppenheimer'}
name_dict def remove_middle_name(name_dict):
'middle_name'] = ''
name_dict[
remove_middle_name(name_dict)**name_dict) print_name(
Adam Oppenheimer
Once an argument has a default value, all following arguments must also have default values
def print_name(first_name, middle_name='', last_name):
print(first_name, middle_name, last_name)
SyntaxError: parameter without a default follows parameter with a default (4160617308.py, line 1)
In-place updates to default values are maintained between function calls - this can lead to big headaches!
def append_0(a=[]):
0)
a.append(print('a:', a)
append_0() append_0()
a: [0]
a: [0, 0]
Avoid this issue by setting the default value to None
if you know the argument can have in-place updates (unless this is the behavior you want, although it is extremely unlikely that you want this to happen)
def append_0(a=None):
if a is None:
= []
a 0)
a.append(print('a:', a)
append_0() append_0()
a: [0]
a: [0]
2. Tricks for lists
2.1 List comprehension
Super convenient way to create lists (also works for dictionaries)
= []
lst for a in range(5):
lst.append(a)print(lst)
[0, 1, 2, 3, 4]
for a in range(5)] [a
[0, 1, 2, 3, 4]
if (a % 2 == 0) else 1 / a for a in range(5)] [a
[0, 1.0, 2, 0.3333333333333333, 4]
for a in range(5) if a != 3] [a
[0, 1, 2, 4]
if (a % 2 == 0) else 1 / a for a in range(5) if a != 2] [a
[0, 1.0, 0.3333333333333333, 4]
This is much more concise than the for-loop version
= []
lst for a in range(5):
if a != 2:
if a % 2 == 0:
lst.append(a)else:
1 / a)
lst.append(print(lst)
[0, 1.0, 0.3333333333333333, 4]
Can even nest list comprehensions. Notice that you order the for loops in the same order as if you were writing out the full loops.
for i in range(3) for j in range(2)] [(i, j)
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
for i in range(3):
for j in range(2):
print(i, j)
0 0
0 1
1 0
1 1
2 0
2 1
for i in range(3) for j in range(i)] [(i, j)
[(1, 0), (2, 0), (2, 1)]
Be careful about some weird behavior with list comprehensions (this can also happen with regular loops) (see https://stackoverflow.com/questions/3431676/creating-functions-or-lambdas-in-a-loop-or-comprehension).
2.2 sorted()
Sort a list and specify the key. Notice the lambda functions to set the key for sorting.
sorted([4, 1, 8])
[1, 4, 8]
def get_first(a):
return a[0]
def get_second(a):
return a[1]
sorted([('d', 4), ('a', 1), ('r', 2)], key=get_first)
[('a', 1), ('d', 4), ('r', 2)]
sorted([('d', 4), ('a', 1), ('r', 2)], key=lambda a: a[0])
[('a', 1), ('d', 4), ('r', 2)]
sorted([('d', 4), ('a', 1), ('r', 2)], key=lambda a: a[1])
[('a', 1), ('r', 2), ('d', 4)]
2.3 zip()
Combine two lists element by element. Make sure to convert to a list for PA3.
list(zip(range(0, 5), range(5, 0, -1)))
[(0, 5), (1, 4), (2, 3), (3, 2), (4, 1)]
Make sure the lists are the same length! They’ll zip even if they aren’t!
list(zip(range(0, 7), range(5, 0, -1)))
[(0, 5), (1, 4), (2, 3), (3, 2), (4, 1)]
Note: zip
creates a generator, which is why we need to convert it to a list
= zip(range(0, 5), range(5, 0, -1))
zipped_lst for elmt in zipped_lst:
print(elmt)
break
for elmt in zipped_lst:
print(elmt)
break
print(zipped_lst[0])
(0, 5)
(1, 4)
TypeError: 'zip' object is not subscriptable
2.4 any()
and all()
any()
: check whether any element of a list is True.
all()
: check whether all elements of a list are True.
any([True, True, False])
True
any([False, False, False])
False
all([True, True, False])
False
all([True, True, True])
True
3. Tricks for strings
3.1 lower()
Convert text to lowercase
'HELLO'.lower()
'hello'
3.2 split()
Split string into a list, dividing words by the specified character
'Hello, I am Adam.'.split(' ')
['Hello,', 'I', 'am', 'Adam.']
'Hello, I am Adam.'.split('m')
['Hello, I a', ' Ada', '.']
3.3 strip()
Remove the specified characters from the start and end of a string
'abcdefgabcdefg'.strip('bacdg')
'efgabcdef'
3.4 startswith()
Check whether the string starts with the specified string
'hello'.startswith('he')
True
'hello'.startswith('hen')
False
4. Type casting
Convert between types by writing the name of the type you want to convert to and using the name as if it is a function
= 1.0
val print(val)
print(int(val))
1.0
1
= [1, 2, 3]
lst print(lst)
print(tuple(lst))
[1, 2, 3]
(1, 2, 3)
print(zip(range(0, 5), range(5, 0, -1)))
print(list(zip(range(0, 5), range(5, 0, -1))))
<zip object at 0x1079a6200>
[(0, 5), (1, 4), (2, 3), (3, 2), (4, 1)]
5. PA2 review
Discussion and review my solution