0% found this document useful (0 votes)
2 views244 pages

3 Python

The document discusses a Python program that finds anagrams from a given list of letters, emphasizing the use of sorted letters to identify anagrams. It also covers various Python concepts including data structures, advanced looping techniques, comprehensions, function definitions, and the distinction between scope and namespaces. Additionally, it touches on the pass-by-object-reference behavior in Python functions.

Uploaded by

abdallah.khmaies
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views244 pages

3 Python

The document discusses a Python program that finds anagrams from a given list of letters, emphasizing the use of sorted letters to identify anagrams. It also covers various Python concepts including data structures, advanced looping techniques, comprehensions, function definitions, and the distinction between scope and namespaces. Additionally, it touches on the pass-by-object-reference behavior in Python functions.

Uploaded by

abdallah.khmaies
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Warmup

Write a program that, given a list of letters, finds all anagrams.

$ python [Link]
Letters? unicorn
Anagram(s): UNICORN
Letters? python
Anagram(s): PYTHON, PHYTON
Letters? aoiuvbosidbvoahbs
Anagram(s): None

A key insight: the sorted letters of any


two anagrams is the same.
Consider making a dict
{sorted letters: set of words}
Functions
Today :
Today :
• Review of Data Structures
• Advanced Looping
• Comprehensions
• Inside Python Functions
• Parameters
• Variadic Arguments
• Parameter Ordering
• Aside: Code Style
• First-Class Functions
Review of Data Structures
Data Structures
Data Structures

List [whatever, objects, you, want]


Tuple (frozen, sequence, of, objects)
Dictionary {key: value}
Set {unique, hashable, values}
Data Structures

List [whatever, objects, you, want]


Tuple (frozen, sequence, of, objects)
Dictionary {key: value}
Set {unique, hashable, values}
Data Structures

List [whatever, objects, you, want]


Tuple (frozen, sequence, of, objects)
Dictionary {key: value}
Set {unique, hashable, values}
Data Structures

List [whatever, objects, you, want]


Tuple (frozen, sequence, of, objects)
Dictionary {key: value}
Set {unique, hashable, values}
Advanced Looping
zip

questions = ['name', 'quest', 'favorite color']


answers = ['lancelot', 'the holy grail', 'blue']
zip

questions = ['name', 'quest', 'favorite color'] Generates pairs


answers = ['lancelot', 'the holy grail', 'blue'] of entries from its
arguments
for q, a in zip(questions, answers):
zip

questions = ['name', 'quest', 'favorite color'] Generates pairs


answers = ['lancelot', 'the holy grail', 'blue'] of entries from its
arguments
for q, a in zip(questions, answers):
print('What is your {0}? It is {1}.'.format(q, a))
zip

questions = ['name', 'quest', 'favorite color'] Generates pairs


answers = ['lancelot', 'the holy grail', 'blue'] of entries from its
arguments
for q, a in zip(questions, answers):
print('What is your {0}? It is {1}.'.format(q, a))

# What is your name? It is lancelot.


# What is your quest? It is the holy grail.
# What is your favorite color? It is blue.
sorted

basket = ['apple', 'orange', 'apple', 'pear', 'orange’,


'banana']
sorted

basket = ['apple', 'orange', 'apple', 'pear', 'orange’,


'banana']
for f in sorted(set(basket)):
print(f)

Returns a new sorted list while


leaving the source unaltered
sorted

basket = ['apple', 'orange', 'apple', 'pear', 'orange’,


'banana']
for f in sorted(set(basket)):
print(f)

# apple
# banana
Returns a new sorted list while
# orange leaving the source unaltered
# pear
Comprehensions
Square Numbers

squares = []
Square Numbers

squares = []
for x in range(10):
[Link](x**2)
Square Numbers

squares = []
for x in range(10):
[Link](x**2)

squares # => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


Square Numbers (Comprehension)

[x ** 2 for x in range(10)]
Square Numbers (Comprehension)

[x ** 2 for x in range(10)]
Square Numbers (Comprehension)

[x ** 2 for x in range(10)]
Square Numbers (Comprehension)

[x ** 2 for x in range(10)]
Square Numbers (Comprehension)

Square brackets for a list

[x ** 2 for x in range(10)]
Square Numbers (Comprehension)

Square brackets for a list

[x ** 2 for x in range(10)]
Apply this operation
Square Numbers (Comprehension)

Square brackets for a list

[x ** 2 for x in range(10)]
Apply this operation With this loop condition
List comprehensions

Square brackets for a list

[fn(x) for x in iterable]


Apply this operation With this loop condition
List comprehensions

[fn(x) for x in iterable if cond(x)]


Only keep elements that
satisfy a boolean condition
Examples

[[Link]() for word in sentence]


Examples

[[Link]() for word in sentence]


[ch for ch in [Link]() if ch not in 'aeiou']
Examples

[[Link]() for word in sentence]


[ch for ch in [Link]() if ch not in 'aeiou']

[(x, x**2, x**3) for x in seq]


Examples

[[Link]() for word in sentence]


[ch for ch in [Link]() if ch not in 'aeiou']

[(x, x**2, x**3) for x in seq]


[(i,j) for i in range(5) for j in range(i)]

Be careful! Simple is better than complex.


Your turn!

[0, 1, 2, 3] -> [1, 3, 5, 7]


[3, 8, 9, 5] -> [True, False, True, False]
Your turn!

[0, 1, 2, 3] -> [1, 3, 5, 7]


[3, 8, 9, 5] -> [True, False, True, False]

['apple', 'orange', 'pear'] -> ['A', 'O', 'P']


['apple', 'orange', 'pear'] ->
[('apple', 5), ('orange', 6), ('pear', 4)]
Other Comprehensions
Other Comprehensions

# Dictionary comprehensions
{key_fun(x):val_fun(x) for x in iterable}
Other Comprehensions

# Dictionary comprehensions
{key_fun(x):val_fun(x) for x in iterable}
fav_animals = {
'parth': 'unicorn',
'michael': 'elephant',
'zheng': 'tree',
'sam': 'ox',
'nick': 'Daisy'
}
Other Comprehensions

# Dictionary comprehensions
{key_fun(x):val_fun(x) for x in iterable}
fav_animals = {
'parth': 'unicorn',
'michael': 'elephant',
'zheng': 'tree',
'sam': 'ox',
'nick': 'Daisy'
}
fav_humans = {val:key for key, val in fav_animals.items()}
Other Comprehensions

# Dictionary comprehensions
{key_fun(x):val_fun(x) for x in iterable}
fav_animals = {
'parth': 'unicorn',
'michael': 'elephant',
'zheng': 'tree',
'sam': 'ox',
'nick': 'Daisy'
}
fav_humans = {val:key for key, val in fav_animals.items()}

# Set comprehensions
{fun(x) for x in iterable}
A High-Level View
Why would we use comprehensions?
A High-Level View
Why would we use comprehensions?
Usual focus: Modify individual elements of an iterable.
A High-Level View
Why would we use comprehensions?
Usual focus: Modify individual elements of an iterable.
Comprehensions ~ Abstract Transformations
Don’t say how you want the object constructed.
Just say what you want in the object.
A High-Level View
Why would we use comprehensions?
Usual focus: Modify individual elements of an iterable.
Comprehensions ~ Abstract Transformations
Don’t say how you want the object constructed.
Just say what you want in the object.
If you liked this, stay tuned… Functional Programming!
Inside Python Functions
So far…

The def keyword is used to define a new function.

def fn_name(param1, param2):


...
return some_value
Some nuances…
Some nuances…

All functions return a value…


If not specified or just “return” implicitly, functions return None.
Some nuances…

All functions return a value…


If not specified or just “return” implicitly, functions return None.
Returning multiple values…

You can use a tuple!


Some nuances…

All functions return a value…


If not specified or just “return” implicitly, functions return None.
Returning multiple values…

You can use a tuple!


return val1, val2, val3
Some nuances…

All functions return a value…


If not specified or just “return” implicitly, functions return None.
Returning multiple values…

You can use a tuple!


return val1, val2, val3
Be careful! Sometimes callers might not expect a tuple as a
return value. Using a namedtuple may be useful.
Function Execution and Scope
Function Execution and Scope

Function execution introduces a new local symbol table (literally:


a dict that associates names to values).
Function Execution and Scope

Function execution introduces a new local symbol table (literally:


a dict that associates names to values).

Variable assignments add to the local symbol table (or


overwrite existing values).
x = 5
Function Execution and Scope

Function execution introduces a new local symbol table (literally:


a dict that associates names to values).

Variable assignments add to the local symbol table (or


overwrite existing values).
x = 5
Variable references check a tower of scopes.
print(y)
Variable Resolution

Builtins

Global Scope

Enclosing Function Scope

Local Function Scope


Variable Resolution

Builtins

Global Scope

Enclosing Function Scope

Local Function Scope

x = 5
Variable Resolution

Builtins

Global Scope

Enclosing Function Scope

Local Function Scope

x = 5
Variable Resolution

Builtins

Global Scope

Enclosing Function Scope

Local Function Scope

x = 5 print(y)
Variable Resolution

Builtins

Global Scope

Enclosing Function Scope

Local Function Scope Do you have a y?

x = 5 print(y)
Variable Resolution

Builtins

Global Scope

Enclosing Function Scope Do you have a y?

Local Function Scope Do you have a y?

x = 5 print(y)
Variable Resolution

Builtins

Global Scope Do you have a y?

Enclosing Function Scope Do you have a y?

Local Function Scope Do you have a y?

x = 5 print(y)
Variable Resolution

Builtins Do you have a y?

Global Scope Do you have a y?

Enclosing Function Scope Do you have a y?

Local Function Scope Do you have a y?

x = 5 print(y)
Variable Resolution

NameError

Builtins Do you have a y?

Global Scope Do you have a y?

Enclosing Function Scope Do you have a y?

Local Function Scope Do you have a y?

x = 5 print(y)
Function Scopes: An Example

x = 2
Function Scopes: An Example

x = 2
def foo(y):
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)
Function Scopes: An Example

x = 2
def foo(y):
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)

foo(3)
Function Scopes: An Example

x = 2
def foo(y):
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)

foo(3)
# {'y': 3, 'z': 5}
# 2
# 2 3 5
Function Scopes: An Example

x = 2
def foo(y):
x = 41
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)
Function Scopes: An Example

x = 2
def foo(y):
x = 41
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)

foo(3)
Function Scopes: An Example

x = 2
def foo(y):
x = 41
z = 5
print(locals())
print(globals()['x'])
print(x, y, z)

foo(3)
# {'y': 3, 'x': 41, 'z': 5}
# 2
# 41 3 5
Scope vs. Namespaces
Scope vs. Namespaces

A namespace is a dictionary, mapping names (as strings) to


values.
Scope vs. Namespaces

A namespace is a dictionary, mapping names (as strings) to


values.
A scope defines which namespaces will be looked in and in what
order.
Scope vs. Namespaces

A namespace is a dictionary, mapping names (as strings) to


values.
A scope defines which namespaces will be looked in and in what
order.
So, scopes can be overlapping (a global variable can be reached
by any local reference), but namespaces are not overlapping.
Scope vs. Namespaces

A namespace is a dictionary, mapping names (as strings) to


values.
A scope defines which namespaces will be looked in and in what
order.
So, scopes can be overlapping (a global variable can be reached
by any local reference), but namespaces are not overlapping.
Only* function definitions define new scopes/namespaces.

*And classes; classes are


more like wrappers around
namespaces. Stay tuned
for more!
New Scopes

if statements, for loops, while loops, with statements, etc.


do not introduce a new scope.
New Scopes

if statements, for loops, while loops, with statements, etc.


do not introduce a new scope.

if success:
desc = 'Winner! :)'
else:
desc = 'Loser :('

print(desc)
New Scopes

if statements, for loops, while loops, with statements, etc.


do not introduce a new scope.

if success:
desc = 'Winner! :)' desc is bound to the innermost
else: scope, enclosing the if
desc = 'Loser :(' statement…

print(desc)
New Scopes

if statements, for loops, while loops, with statements, etc.


do not introduce a new scope.

if success:
desc = 'Winner! :)' desc is bound to the innermost
else: scope, enclosing the if
desc = 'Loser :(' statement…

print(desc) …which is why this line will print


out the value assigned above
Pass-by-Value or Pass-by-Reference?
Pass-by-Value or Pass-by-Reference?

Variables are copied into function’s local symbol table…


…but variables are just references to objects!
Pass-by-Value or Pass-by-Reference?

Variables are copied into function’s local symbol table…


…but variables are just references to objects!

Best to think of it as pass-by-object-reference…


If a mutable object is passed, caller will see changes.
Pass-by-Value or Pass-by-Reference?
Pass-by-Value or Pass-by-Reference?
I
M
M
U
T
A
B
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5
M
M x += 1
U x # => 6
T
A
B
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5
M A new object
M x += 1 is created and
U x # => 6 rebound to the
T
namespace as
A
B x
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object
M x += 1 is created and x += 1
U x # => 6 rebound to the
T
namespace as x = 5
A
B x
L
foo(x)
E x # => 5
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.

M
U
T
A
B
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.

x = [5]
M
U [Link](41)
T x # => [5, 41]
A
B
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.

x = [5]
M No new object
U [Link](41) is created.
T x # => [5, 41] The value of x
A
is modified…
B
L
E
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.

x = [5] def foo(x)


M No new object
U [Link](41) is created. [Link](41)
T x # => [5, 41] The value of x
A
is modified… x = [5]
B
L
E
foo(x)
x # => [5, 41]
Pass-by-Value or Pass-by-Reference?
I x = 5 def foo(x):
M A new object A new object
M x += 1 is created and x += 1 is created and
U x # => 6 rebound to the rebound, but x
T
namespace as x = 5 only scopes
A
B x over foo, so it
L
foo(x) only updates
E x # => 5 locally.

x = [5] def foo(x) Because x is


M No new object
U [Link](41) is created. [Link](41) not being
T x # => [5, 41] The value of x rebound,
A
is modified… changes will
B x = [5] propagate.
L
E
foo(x)
x # => [5, 41]
Parameters
So far…

So far, we’ve seen required positional parameters.

def compute(a, b, c):


return (a + b) * c
So far…

So far, we’ve seen required positional parameters.


a, b, and c are the parameters.

def compute(a, b, c):


return (a + b) * c
So far…

So far, we’ve seen required positional parameters.


a, b, and c are the parameters.

def compute(a, b, c):


return (a + b) * c

(they’re required)
Default Parameters
Default Parameters

Specify a default value for one or more parameters.


Can be called with fewer arguments than it has!
Default Parameters

Specify a default value for one or more parameters.


Can be called with fewer arguments than it has!
Usually to provide “settings” for the function and specify certain
default values.
Default Parameters

Specify a default value for one or more parameters.


Can be called with fewer arguments than it has!
Usually to provide “settings” for the function and specify certain
default values.
Why?
Presents a simplified interface.
Provides reasonable defaults for parameters.
Declares that certain parameters are “extra.”
Default Parameters

def ask_yn(prompt,
retries=4,
complaint="Enter Y or N!"):
Default Parameters

def ask_yn(prompt, prompt is required

retries=4,
complaint="Enter Y or N!"):
retries is optional, complaint is also optional,
defaults to 4 defaults to "Enter Y or N!"
Default Parameters

def ask_yn(prompt, prompt is required

retries=4,
complaint="Enter Y or N!"):
retries is optional, complaint is also optional,
defaults to 4 defaults to "Enter Y or N!"

# A valid call:
ask_yn('Ok to overwrite the file?', 2)
Default Parameters

def ask_yn(prompt, prompt is required

retries=4,
complaint="Enter Y or N!"):
retries is optional, complaint is also optional,
defaults to 4 defaults to "Enter Y or N!"

# A valid call:
ask_yn('Ok to overwrite the file?', 2)
prompt Overwrite default value
of retries
Default Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):


for i in range(retries):
ans = input(prompt)

if ans in 'yY':
return True
if ans in 'nN':
return False

print(complaint)
Default Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):


for i in range(retries):
ans = input(prompt)

if ans in 'yY':
return True
if ans in 'nN':
return False

print(complaint)

Within the function: treat the parameters just like always!


Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):


Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

All of the above parameters can be called based on position or


as keyword arguments.
Specify the name of the variable in the call.
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

All of the above parameters can be called based on position or


as keyword arguments.
Specify the name of the variable in the call.
Why?
Make parameter intent clearer – name conveys more than position.
Reduce risk of incorrect calls.
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
ask_yn('Ok to overwrite the file?', 2)
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
ask_yn('Ok to overwrite the file?', 2)
ask_yn('Ok to overwrite the file?', retries=2)
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
ask_yn('Ok to overwrite the file?', 2)
ask_yn('Ok to overwrite the file?', retries=2)
ask_yn(prompt="Are you sure about that?", retries=2)
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
ask_yn('Ok to overwrite the file?', 2)
ask_yn('Ok to overwrite the file?', retries=2)
ask_yn(prompt="Are you sure about that?", retries=2)
ask_yn(retries=2, prompt="Are you sure about that?")
Keyword Arguments

def ask_yn(prompt, retries=4, complaint="Enter Y or N!"):

# Valid calls:
ask_yn('Ok to overwrite the file?', 2)
ask_yn('Ok to overwrite the file?', retries=2) Order
ask_yn(prompt="Are you sure about that?", retries=2) does not
matter for
ask_yn(retries=2, prompt="Are you sure about that?") these
calls!
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
enumerate(iter, start=0)
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
enumerate(iter, start=0)
int(x, base=10)
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
enumerate(iter, start=0)
int(x, base=10)
pow(x, y, z=None)
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
enumerate(iter, start=0)
int(x, base=10)
pow(x, y, z=None)
[Link](*, key=None, reverse=None)
Examples

print(..., sep=' ', end='\n', file=[Link], flush=False)


range(start, stop, step=1)
enumerate(iter, start=0)
int(x, base=10)
pow(x, y, z=None)
[Link](*, key=None, reverse=None)

[Link](args, bufsize=-1, executable=None,


stdin=None, stdout=None, stderr=None, preexec_fn=None,
close_fds=True, shell=False, cwd=None, env=None,
universal_newlines=None, startupinfo=None,
creationflags=0, restore_signals=True,
start_new_session=False, pass_fds=(), encoding=None,
errors=None, text=None) Wow…
Variadic Arguments
Variadic Positional Arguments
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Why?
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Why?
Call functions with any number of positional arguments
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Why?
Call functions with any number of positional arguments
Capture all arguments to forward to another handler
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Why?
Call functions with any number of positional arguments
Capture all arguments to forward to another handler
Used in subclasses, proxies, and decorators
Variadic Positional Arguments

A parameter of the form *args captures excess positional


arguments.
These arguments are bundled into the args tuple.
Why?
Call functions with any number of positional arguments
Capture all arguments to forward to another handler
Used in subclasses, proxies, and decorators

print(*objects, sep=' ', end='\n', file=[Link], flush=False)


Variadic Positional Arguments

# Suppose we want a function that works as so:


scaled_sum(1, 2, 3) # => 6
scaled_sum(1, 5) # => 6
scaled_sum(1, 5, scale=10) # => 60
Variadic Positional Arguments

# Suppose we want a function that works as so:


scaled_sum(1, 2, 3) # => 6
scaled_sum(1, 5) # => 6
scaled_sum(1, 5, scale=10) # => 60
# i.e., scaled_sum(x1, ..., xn, scale=a) = a(x1 + ... + xn)
Variadic Positional Arguments

# Suppose we want a function that works as so:


scaled_sum(1, 2, 3) # => 6
scaled_sum(1, 5) # => 6
scaled_sum(1, 5, scale=10) # => 60
# i.e., scaled_sum(x1, ..., xn, scale=a) = a(x1 + ... + xn)

# scaled_sum accepts any number of arguments


def scaled_sum(*args, scale=1):
Variadic Positional Arguments

# Suppose we want a function that works as so:


scaled_sum(1, 2, 3) # => 6
scaled_sum(1, 5) # => 6
scaled_sum(1, 5, scale=10) # => 60
# i.e., scaled_sum(x1, ..., xn, scale=a) = a(x1 + ... + xn)

# scaled_sum accepts any number of arguments


def scaled_sum(*args, scale=1):
return scale * sum(args)
Unpacking Variadic Positional Arguments

# Suppose we want to find the sum of the primes up to 100


def is_prime(p): ... # some implementation
Unpacking Variadic Positional Arguments

# Suppose we want to find the sum of the primes up to 100


def is_prime(p): ... # some implementation

primes = [p in range(1, 100) if is_prime(p)]


Unpacking Variadic Positional Arguments

# Suppose we want to find the sum of the primes up to 100


def is_prime(p): ... # some implementation

primes = [p in range(1, 100) if is_prime(p)]

print(scaled_sum(*primes))
# equivalent to print(scaled_sum(2, 3, 5, ...))
Variadic Keyword Arguments
Variadic Keyword Arguments

A parameter of the form **kwargs captures all excess keyword


arguments.
These excess arguments are bundled into a kwargs dict.
Variadic Keyword Arguments

A parameter of the form **kwargs captures all excess keyword


arguments.
These excess arguments are bundled into a kwargs dict.
Why?
Allow arbitrary named parameters, usually for configuration.
Variadic Keyword Arguments

A parameter of the form **kwargs captures all excess keyword


arguments.
These excess arguments are bundled into a kwargs dict.
Why?
Allow arbitrary named parameters, usually for configuration.
Similar: capture all arguments to forward to another handler.
Variadic Keyword Arguments

A parameter of the form **kwargs captures all excess keyword


arguments.
These excess arguments are bundled into a kwargs dict.
Why?
Allow arbitrary named parameters, usually for configuration.
Similar: capture all arguments to forward to another handler.
Used in subclasses, proxies, and decorators.
Variadic Keyword Arguments

A parameter of the form **kwargs captures all excess keyword


arguments.
These excess arguments are bundled into a kwargs dict.
Why?
Allow arbitrary named parameters, usually for configuration.
Similar: capture all arguments to forward to another handler.
Used in subclasses, proxies, and decorators.

"{good} are better than {bad}".format(good='Reindeers', bad='people')


Variadic Keyword Arguments
Variadic Keyword Arguments

stylize_quote (
"If music be the food of love, play on.",
act=1,
scene=1,
speaker="Duke Orsino",
playwright="Shakespeare"
)
Variadic Keyword Arguments

stylize_quote (
"If music be the food of love, play on.",
act=1,
scene=1,
speaker="Duke Orsino",
playwright="Shakespeare"
)

# > If music be the food of love, play on.


# ----------------------------------------
# act: 1
# scene: 1
# speaker: Duke Orsino
# playwright: Shakespeare
Variadic Keyword Arguments

def stylize_quote(quote, **speaker_info):


Variadic Keyword Arguments

def stylize_quote(quote, **speaker_info):

speaker_info = {
'act': 1,
'scene': 1,
'speaker': 'Duke Orsino',
'playwright': 'Shakespeare'
}
Variadic Keyword Arguments

def stylize_quote(quote, **speaker_info):


print("> {}".format(quote))
print('-' * (len(quote) + 2))

speaker_info = {
'act': 1,
'scene': 1,
'speaker': 'Duke Orsino',
'playwright': 'Shakespeare'
}
Variadic Keyword Arguments

def stylize_quote(quote, **speaker_info):


print("> {}".format(quote))
print('-' * (len(quote) + 2))

for k, v in speaker_info.items():
print("{}: {}".format(k, v))

speaker_info = {
'act': 1,
'scene': 1,
'speaker': 'Duke Orsino',
'playwright': 'Shakespeare'
}
Unpacking Variadic Keyword Arguments

info = {
'speaker': 'Iron Man',
'year': 2012,
'movie': 'The Avengers'
}
Unpacking Variadic Keyword Arguments

info = {
'speaker': 'Iron Man',
'year': 2012,
'movie': 'The Avengers'
}

stylize_quote("Doth mother know you weareth her drapes?", **info)


Unpacking Variadic Keyword Arguments

info = {
'speaker': 'Iron Man',
'year': 2012,
'movie': 'The Avengers'
}

stylize_quote("Doth mother know you weareth her drapes?", **info)

# > Doth mother know you weareth her drapes?


# ------------------------------------------
# speaker: Iron Man
# year: 2012
# movie: The Avengers
Example: Formatting Strings
String Formatting

[Link](*args, **kwargs)
[Link](*args, **kwargs)
[Link](*args, **kwargs)

# {n} refers to the nth positional argument in `args`


"First, thou shalt count to {0}".format(3) args = (3, )
[Link](*args, **kwargs)

# {n} refers to the nth positional argument in `args`


"First, thou shalt count to {0}".format(3) args = (3, )

"{0} shalt thou not count, neither count thou {1},


excepting that thou then proceed to {2}".format(4, 2, 3)
args = (4, 2, 3)
[Link](*args, **kwargs)

# {n} refers to the nth positional argument in `args`


"First, thou shalt count to {0}".format(3) args = (3, )

"{0} shalt thou not count, neither count thou {1},


excepting that thou then proceed to {2}".format(4, 2, 3)
args = (4, 2, 3)
# {key} refers to the optional argument bound by key
"lobbest thou thy {weapon} towards thy foe".format(
weapon="Holy Hand Grenade of Antioch"
) kwargs = {"weapon": "Holy
Hand Grenade of Antioch"}
[Link](*args, **kwargs)

# {n} refers to the nth positional argument in `args`


"First, thou shalt count to {0}".format(3) args = (3, )

"{0} shalt thou not count, neither count thou {1},


excepting that thou then proceed to {2}".format(4, 2, 3)
args = (4, 2, 3)
# {key} refers to the optional argument bound by key
"lobbest thou thy {weapon} towards thy foe".format(
weapon="Holy Hand Grenade of Antioch"
) kwargs = {"weapon": "Holy
Hand Grenade of Antioch"}
"{0}{b}{1}{a}{0}{2}".format(
5, 8, 9, a='z', b='x'
args = (5, 8, 9)
) kwargs = {'a': 'z', 'b': 'x'}
[Link](*args, **kwargs)

# {n} refers to the nth positional argument in `args`


"First, thou shalt count to {0}".format(3) args = (3, )

"{0} shalt thou not count, neither count thou {1},


excepting that thou then proceed to {2}".format(4, 2, 3)
args = (4, 2, 3)
# {key} refers to the optional argument bound by key
"lobbest thou thy {weapon} towards thy foe".format(
weapon="Holy Hand Grenade of Antioch"
) kwargs = {"weapon": "Holy
Hand Grenade of Antioch"}
"{0}{b}{1}{a}{0}{2}".format(
5, 8, 9, a='z', b='x'
args = (5, 8, 9)
) kwargs = {'a': 'z', 'b': 'x'}
# => 5x8z59
[Link](*args, **kwargs)
[Link](*args, **kwargs)

# A cute trick…
x = 3
foo = 'fighter'
y = 4
learn = 2, 'fly'
z = 5
[Link](*args, **kwargs)

# A cute trick… locals() == {


x = 3 'x': 3,
foo = 'fighter' 'foo': 'fighter',
y = 4 'y': 4,
learn = 2, 'fly' 'learn': (2, 'fly'),
z = 5 'z': 5,
...
}
[Link](*args, **kwargs)

# A cute trick… locals() == {


x = 3 'x': 3,
foo = 'fighter' 'foo': 'fighter',
y = 4 'y': 4,
learn = 2, 'fly' 'learn': (2, 'fly'),
z = 5 'z': 5,
...
}

print("{z}^2 = {x}^2 + {y}^2".format(x=x, y=y, z=z))


[Link](*args, **kwargs)

# A cute trick… locals() == {


x = 3 'x': 3,
foo = 'fighter' 'foo': 'fighter',
y = 4 'y': 4,
learn = 2, 'fly' 'learn': (2, 'fly'),
z = 5 'z': 5,
...
}

print("{z}^2 = {x}^2 + {y}^2".format(x=x, y=y, z=z))


print("{z}^2 = {x}^2 + {y}^2".format(**locals()))
# Equivalent to .format(x=3, foo='fighter', y=4, ...)
[Link](*args, **kwargs)

# A cute trick… locals() == {


x = 3 'x': 3,
foo = 'fighter' 'foo': 'fighter',
y = 4 'y': 4,
learn = 2, 'fly' 'learn': (2, 'fly'),
z = 5 'z': 5,
...
}

print("{z}^2 = {x}^2 + {y}^2".format(x=x, y=y, z=z))


print("{z}^2 = {x}^2 + {y}^2".format(**locals()))
# Equivalent to .format(x=3, foo='fighter', y=4, ...)

This is kind of how fstrings work!


Parameter Ordering
Parameter Rules

1. Keyword arguments must follow positional arguments.


2. All arguments must identify some parameter.
Even positional ones.
3. No parameter may receive a value more than once.
Parameter Rules

1. Keyword arguments must follow positional arguments.


2. All arguments must identify some parameter.
Even positional ones.
3. No parameter may receive a value more than once.
Parameter Rules

1. Keyword arguments must follow positional arguments.


2. All arguments must identify some parameter.
Even positional ones.
3. No parameter may receive a value more than once.
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
parrot(voltage=1000)
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
parrot(voltage=1000)
parrot(voltage=1000000, action='VOOOOOM')
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
parrot(voltage=1000)
parrot(voltage=1000000, action='VOOOOOM')
parrot(action='VOOOOOM', voltage=1000000)
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
parrot(voltage=1000)
parrot(voltage=1000000, action='VOOOOOM')
parrot(action='VOOOOOM', voltage=1000000)
parrot('a million', 'bereft of life', 'jump')
Parrots Go VOOM

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

parrot(1000)
parrot(voltage=1000)
parrot(voltage=1000000, action='VOOOOOM')
parrot(action='VOOOOOM', voltage=1000000)
parrot('a million', 'bereft of life', 'jump')
parrot('a thousand', state='pushing up the daisies')
Parrots Go VOOM: Incorrect Calls

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
Parrots Go VOOM: Incorrect Calls

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

# required argument missing


parrot()
Parrots Go VOOM: Incorrect Calls

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

# required argument missing


parrot()
# non-keyword argument after a keyword argument
parrot(voltage=5.0, 'dead')
Parrots Go VOOM: Incorrect Calls

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

# required argument missing


parrot()
# non-keyword argument after a keyword argument
parrot(voltage=5.0, 'dead')
# duplicate value for the same argument
parrot(110, voltage=220)
Parrots Go VOOM: Incorrect Calls

def parrot(voltage, state='a stiff', action='voom’,


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

# required argument missing


parrot()
# non-keyword argument after a keyword argument
parrot(voltage=5.0, 'dead')
# duplicate value for the same argument
parrot(110, voltage=220)
# unknown keyword argument
parrot(actor='John Cleese')
Parameter Rules

Symbols
Parameter Rules

Symbols
/ New in Python 3.8: Arguments before this are positional-only.
PEP 570
Parameter Rules

Symbols
/ New in Python 3.8: Arguments before this are positional-only.
PEP 570

* Arguments before this are positional or keyword arguments.


Arguments after this are keyword only.
(Making sure to follow rule 1)
Parameter Rules

Symbols
/ New in Python 3.8: Arguments before this are positional-only.
PEP 570

* Arguments before this are positional or keyword arguments.


Arguments after this are keyword only.
(Making sure to follow rule 1)

*a Captures trailing positional arguments.


Parameter Rules

Symbols
/ New in Python 3.8: Arguments before this are positional-only.
PEP 570

* Arguments before this are positional or keyword arguments.


Arguments after this are keyword only.
(Making sure to follow rule 1)

*a Captures trailing positional arguments.


**kw Captures extra keyword arguments.
Parameter Rules

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)


Parameter Rules

Default values – optional args

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)


Parameter Rules

Default values – optional args

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only
Parameter Rules

Default values – optional args

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only Positional or Keyword

Note: it would not be valid if


called with c keyword and d
positional!
Parameter Rules
Captures additional positional
Default values – optional args args (and throws them away)

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only Positional or Keyword

Note: it would not be valid if


called with c keyword and d
positional!
Parameter Rules
Captures additional positional
Default values – optional args args (and throws them away)

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only Positional or Keyword Keyword only

Note: it would not be valid if


called with c keyword and d
positional!
Parameter Rules
Captures additional positional
Default values – optional args args (and throws them away)

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only Positional or Keyword Keyword only Captures additional


keyword args

Note: it would not be valid if


called with c keyword and d
positional!
Parameter Rules
Captures additional positional
Default values – optional args args (and throws them away)

def fn(a, b, /, c, d=2, *, e, f=3, **kwargs)

Positional only Positional or Keyword Keyword only Captures additional


keyword args

Note: it would not be valid if


called with c keyword and d
positional!
You’ll evaluate valid/invalid calls in
lab. Use this slide as a reference!
Aside: Code Style
Function Comments

The first string literal inside a function body is a docstring.


First line: one-line summary of the function.
Subsequent lines: extended description of function.
Function Comments

The first string literal inside a function body is a docstring.


First line: one-line summary of the function.
Subsequent lines: extended description of function.
Describe parameters (value / expected type) and return (value /
type).
Many standards have emerged (javado, rest, Google)
Just be consistent!
Function Comments

The first string literal inside a function body is a docstring.


First line: one-line summary of the function.
Subsequent lines: extended description of function.
Describe parameters (value / expected type) and return (value /
type).
Many standards have emerged (javado, rest, Google)
Just be consistent!
The usual rules apply too! List pre-/post-conditions, if any.
Example: Docstrings
Example: Docstrings

def my_function():
"""Summary line: do nothing, but document it.

Description: No, really, it doesn't do anything.


"""
pass
Example: Docstrings

def my_function():
"""Summary line: do nothing, but document it.

Description: No, really, it doesn't do anything.


"""
pass

print(my_function.__doc__)

# Summary line: do nothing, but document it.


#
# Description: No, really, it doesn't do anything.
Example: Docstrings

def my_function():
"""Summary line: do nothing, but document it.

Description: No, really, it doesn't do anything.


"""
pass
More: PEP 257

print(my_function.__doc__)

# Summary line: do nothing, but document it.


#
# Description: No, really, it doesn't do anything.
General Good Practices
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
Commenting
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
Commenting
Comment all nontrivial functions.
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
Commenting
Comment all nontrivial functions.
Add header comments at the top of files before any imports.
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
Commenting
Comment all nontrivial functions.
Add header comments at the top of files before any imports.
If possible, put comments on a line of their own.
General Good Practices

Spacing
Use 4 spaces to indent. Don’t use tabs.
Use blank lines to separate functions and logical sections inside
functions.
Use spaces around operators and after commas, but not directly inside
delimiters.
a = f(1, 2) + g(3, 4) # good
a = f( 1, 2 ) + g( 3, 4 ) # bad
Commenting
Comment all nontrivial functions.
Add header comments at the top of files before any imports.
If possible, put comments on a line of their own. More: PEP 8 and [Link]
General Good Practices

More: PEP 8 and [Link]


General Good Practices

Naming
Use snake_case for variables/functions; CamelCase for classes;
CAPS_CASE for constants.

More: PEP 8 and [Link]


General Good Practices

Naming
Use snake_case for variables/functions; CamelCase for classes;
CAPS_CASE for constants.
Decomposition and Logic
Same as 106A/B/X. Simple is better than complex!

More: PEP 8 and [Link]


General Good Practices

Naming
Use snake_case for variables/functions; CamelCase for classes;
CAPS_CASE for constants.
Decomposition and Logic
Same as 106A/B/X. Simple is better than complex!

Automated Code Style Checking


Use PEP8 Online for mechanical violations (naming, spacing) and more
advanced suggestions.

More: PEP 8 and [Link]


General Good Practices

Naming
Use snake_case for variables/functions; CamelCase for classes;
CAPS_CASE for constants.
Decomposition and Logic
Same as 106A/B/X. Simple is better than complex!

Automated Code Style Checking


Use PEP8 Online for mechanical violations (naming, spacing) and more
advanced suggestions.
Use pycodestyle as a command line tool. Install with pip install
pycodestyle.
More: PEP 8 and [Link]
Remember the Zen of Python
First-Class Functions
First-Class Functions

def echo(arg):
return arg
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>

foo = echo
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>

foo = echo
type(foo) # => <class 'function'>
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>

foo = echo
type(foo) # => <class 'function'>
id(foo) # => 4393732128
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>

foo = echo
type(foo) # => <class 'function'>
id(foo) # => 4393732128
print(foo) # => <function echo at 0x105e30820>
First-Class Functions

def echo(arg):
return arg

type(echo) # => <class 'function'>


id(echo) # => 4393732128
print(echo) # => <function echo at 0x105e30820>

foo = echo
type(foo) # => <class 'function'>
id(foo) # => 4393732128
print(foo) # => <function echo at 0x105e30820>

isinstance(echo, object) # => True


Functions are Objects!
Questions
Questions

What can you do with function objects?


Questions

What can you do with function objects?


What attributes does a function object possess?
Questions

What can you do with function objects?


What attributes does a function object possess?
Can I pass a function as a parameter to another function?
Questions

What can you do with function objects?


What attributes does a function object possess?
Can I pass a function as a parameter to another function?
Can a function return another function?
Questions

What can you do with function objects?


What attributes does a function object possess?
Can I pass a function as a parameter to another function?
Can a function return another function?
How can I modify a function object?
Questions

What can you do with function objects?


What attributes does a function object possess?
Can I pass a function as a parameter to another function?
Can a function return another function?
How can I modify a function object?
Summary
Summary

All functions return some value (possibly None)


Functions define scopes via symbol tables
Parameters are passed by object reference
Functions can have optional keyword arguments
Functions can take a variable number of args and kwargs
Use docstrings and good style
Functions are objects too (?!)
Warmup

Write a program that, given a list of letters, finds all anagrams.

$ python [Link]
Letters? unicorn
Anagram(s): UNICORN
Letters? python
Anagram(s): PYTHON, PHYTON
Letters? aoiuvbosidbvoahbs
Anagram(s): None

A key insight: the sorted letters of any


two anagrams is the same.
Consider making a dict
{sorted letters: set of words}

You might also like