-- Why use functions?
Reduce code duplication
Splitting a complex task
data.science.example.py
def do_report(data_source):
# fetch and prepare data
data = fetch_data(data_source)
parsed_data = parse_data(data)
filtered_data = filter_data(parsed_data)
polished_data = polish_data(filtered_data)
# run algorithms on data
final_data = analyse(polished_data)
# create and return report
report = Report(final_data)
return report
Hide implementation details
Improve readability
matrix.multiplication.nofunc.py
a = [[1, 2], [3, 4]]
b = [[5, 1], [2, 1]]
c = [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)]
for r in a]
matrix.multiplication.func.py
# this function could also be defined in another module
def matrix_mul(a, b):
return [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)]
for r in a]
a = [[1, 2], [3, 4]]
b = [[5, 1], [2, 1]]
c = matrix_mul(a, b)
Improve traceability
vat.py
price = 100 # GBP, no VAT
final_price1 = price * 1.2
final_price2 = price + price / 5.0
final_price3 = price * (100 + 20) / 100.0
final_price4 = price + price * 0.2
vat.function.py
def calculate_price_with_vat(price, vat):
return price * (100 + vat) / 100
-- Scopes and name resolution
scoping.level.1.py
def my_function():
test = 1 # this is defined in the local scope of the function
print('my_function:', test)
test = 0 # this is defined in the global scope
my_function()
print('global:', test)
$ python scoping.level.1.py
scoping.level.2.py
def outer():
test = 1 # outer scope
def inner():
test = 2 # inner scope
print('inner:', test)
inner()
print('outer:', test)
test = 0 # global scope
outer()
print('global:', test)
$ python scoping.level.2.py
The global and nonlocal statements
scoping.level.2.nonlocal.py
def outer():
test = 1 # outer scope
def inner():
nonlocal test
test = 2 # nearest enclosing scope
print('inner:', test)
inner()
print('outer:', test)
test = 0 # global scope
outer()
print('global:', test)
$ python scoping.level.2.nonlocal.py
scoping.level.2.global.py
def outer():
test = 1 # outer scope
def inner():
global test
test = 2 # global scope
print('inner:', test)
inner()
print('outer:', test)
test = 0 # global scope
outer()
print('global:', test)
$ python scoping.level.2.global.py
-- Input parameters
Argument passing
key.points.argument.passing.py
x = 3
def func(y):
print(y)
func(x) # prints: 3
Assignment to argument names don't affect the caller
key.points.assignment.py
x = 3
def func(x):
x = 7 # defining a local x, not changing the global one
func(x)
print(x) # prints: 3
Changing a mutable affects the caller
key.points.mutable.py
x = [1, 2, 3]
def func(x):
x[1] = 42 # this affects the caller!
func(x)
print(x) # prints: [1, 42, 3]
x = [1, 2, 3]
def func(x):
x[1] = 42 # this changes the caller!
x = 'something else' # this points x to a new string object
func(x)
print(x) # still prints: [1, 42, 3]
How to specify input parameters
Positional arguments
arguments.positional.py
def func(a, b, c):
print(a, b, c)
func(1, 2, 3) # prints: 1 2 3
Keyword arguments and default values
arguments.keyword.py
def func(a, b, c):
print(a, b, c)
func(a=1, c=2, b=3) # prints: 1 3 2
arguments.default.py
def func(a, b=4, c=88):
print(a, b, c)
func(1) # prints: 1 4 88
func(b=5, a=7, c=9) # prints: 7 5 9
func(42, c=9) # prints: 42 4 9
func(b=1, c=2, 42) # positional argument after keyword one
Variable positional arguments
arguments.variable.positional.py
def minimum(*n):
# print(n) # n is a tuple
if n: # explained after the code
mn = n[0]
for value in n[1:]:
if value < mn:
mn = value
print(mn)
minimum(1, 3, -7, 9) # n = (1, 3, -7, 9) - prints: -7
minimum() # n = () - prints: nothing
arguments.variable.positional.unpacking.py
def func(*args):
print(args)
values = (1, 3, -7, 9)
func(values) # equivalent to: func((1, 3, -7, 9))
func(*values) # equivalent to: func(1, 3, -7, 9)
Variable keyword arguments
arguments.variable.keyword.py
def func(**kwargs):
print(kwargs)
# All calls equivalent. They print: {'a': 1, 'b': 42}
func(a=1, b=42)
func(**{'a': 1, 'b': 42})
func(**dict(a=1, b=42))
arguments.variable.db.py
def connect(**options):
conn_params = {
'host': options.get('host', '127.0.0.1'),
'port': options.get('port', 5432),
'user': options.get('user', ''),
'pwd': options.get('pwd', ''),
}
print(conn_params)
# we then connect to the db (commented out)
# db.connect(**conn_params)
connect()
connect(host='127.0.0.42', port=5433)
connect(port=5431, user='fab', pwd='gandalf')
$ python arguments.variable.db.py
Keyword-only arguments
arguments.keyword.only.py
def func(a, b, c=7, *args, **kwargs):
print('a, b, c:', a, b, c)
print('args:', args)
print('kwargs:', kwargs)
func(1, 2, 3, *(5, 7, 9), **{'A': 'a', 'B': 'b'})
func(1, 2, 3, 5, 7, 9, A='a', B='b') # same as previous one
$ python arguments.all.py
arguments.all.kwonly.py
def func_with_kwonly(a, b=42, *args, c, d=256, **kwargs):
print('a, b:', a, b)
print('c, d:', c, d)
print('args:', args)
print('kwargs:', kwargs)
# both calls equivalent
func_with_kwonly(3, 42, c=0, d=1, *(7, 9, 11), e='E', f='F')
func_with_kwonly(3, 42, *(7, 9, 11), c=0, d=1, e='E', f='F')
$ python arguments.all.kwonly.py
Avoid the trap! Mutable defaults
arguments.defaults.mutable.py
def func(a=[], b={}):
print(a)
print(b)
print('#' * 12)
a.append(len(a)) # this will affect a's default value
b[len(a)] = len(a) # and this will affect b's one
func()
func()
func()
$ python arguments.defaults.mutable.py
arguments.defaults.mutable.intermediate.call.py
func()
func(a=[1, 2, 3], b={'B': 1})
func()
$ python arguments.defaults.mutable.intermediate.call.py
arguments.defaults.mutable.no.trap.py
def func(a=None):
if a is None:
a = []
# do whatever you want with `a` ...
-- Return values
return.none.py
def func():
pass
func() # the return of this call won't be collected. It's lost.
a = func() # the return of this one instead is collected into `a`
print(a) # prints: None
return.single.value.py
def factorial(n):
if n in (0, 1):
return 1
result = n
for k in range(2, n):
result *= k
return result
f5 = factorial(5) # f5 = 120
return.single.value.2.py
from functools import reduce
from operator import mul
def factorial(n):
return reduce(mul, range(1, n + 1), 1)
f5 = factorial(5) # f5 = 120
Returning multiple values
return.multiple.py
def moddiv(a, b):
return a // b, a % b
print(moddiv(20, 7)) # prints (2, 6)
-- A few useful tips
numbers = [4, 1, 7, 5]
sorted(numbers) # won't sort the original `numbers` list
numbers # let's verify #[4, 1, 7, 5] # good, untouched
numbers.sort() # this will act on the list
numbers #[1, 4, 5, 7]
-- Recursive functions
recursive.factorial.py
def factorial(n):
if n in (0, 1): # base case
return 1
return factorial(n - 1) * n # recursive case
-- Anonymous functions
The logic is exactly the same but the filtering function is now a lambda.
Defining a lambda is very easy and follows this form: func_name = lambda [parameter_list]: expression.
A function object is returned, which is equivalent to this: def func_name([parameter_list]): return expression.
filter.regular.py
def is_multiple_of_five(n):
return not n % 5
def get_multiples_of_five(n):
return list(filter(is_multiple_of_five, range(n)))
print(get_multiples_of_five(50))
filter.lambda.py
def get_multiples_of_five(n):
return list(filter(lambda k: not k % 5, range(n)))
print(get_multiples_of_five(50))
-- Function attributes
func.attributes.py
def multiplication(a, b=1):
"""Return a multiplied by b. """
return a * b
special_attributes = [
"__doc__", "__name__", "__qualname__", "__module__",
"__defaults__", "__code__", "__globals__", "__dict__",
"__closure__", "__annotations__", "__kwdefaults__",
]
for attribute in special_attributes:
print(attribute, '->', getattr(multiplication, attribute))
$ python func.attributes.py
-- Build-in functions
inspecting the builtin module with dir(__builtin__)
One final example
primes.py
from math import sqrt, ceil
def get_primes(n):
"""Calculate a list of primes up to n (included). """
primelist = []
for candidate in range(2, n + 1):
is_prime = True
root = int(ceil(sqrt(candidate))) # division limit
for prime in primelist: # we try only the primes
if prime > root: # no need to check any further
break
if candidate % prime == 0:
is_prime = False
break
if is_prime:
primelist.append(candidate)
return primelist
-- Documenting your code
from math import sqrt, ceil
docstrings.py
def square(n):
"""Return the square of a number n. """
return n ** 2
def get_username(userid):
"""Return the username of a user given their id. """
return db.get(user_id=userid).username
docstrings.py
def connect(host, port, user, password):
"""Connect to a database.
Connect to a PostgreSQL database directly, using the given
parameters.
:param host: The host IP.
:param port: The desired port.
:param user: The connection username.
:param password: The connection password.
:return: The connection object.
"""
# body of the function here...
return connection
-- Importing objects
from mymodule import myfunc as better_named_func
karma/test_nt.py
import unittest # imports the unittest module
from math import sqrt # imports one function from math
from random import randint, sample # two imports at once
from mock import patch
from nose.tools import ( # multiline import
assert_equal,
assert_list_equal,
assert_not_in,
)
from karma import nt, utils
funcdef.py
def square(n):
return n ** 2
def cube(n):
return n ** 3
func_import.py
import lib.funcdef
print(lib.funcdef.square(10))
print(lib.funcdef.cube(10))
func_from.py
from lib.funcdef import square, cube
print(square(10))
print(cube(10))
Relative imports
Relative imports
from .mymodule import myfunc
Summary
Monday, March 21, 2016
Learning Python 4 - Functions
Labels:
Python
Subscribe to:
Post Comments (Atom)
Blog Archive
-
▼
2016
(87)
-
▼
March
(25)
- Learning Python 12 - Summing Up
- Learning Python 11 - Debugging and Troubleshooting
- Learning Python 10 - Web Development Done Right
- Learning Python 9 - Data Science
- Learning Python 8 - The GUIs and Scripts
- Learning Python 7 - Tesing, Profiling, and Dealing...
- Learning Python 6 - OOP, Decorators, and Iterators
- Learning Python 5 - Saving Time and Memory
- Learning Python 4 - Functions
- Learning Python 3 - Interating and Making Decisions
- Learning Python 2 - Build-in Data Types
- Learning Python 1 - Introduction
- Bandit algorithms 7 - Bandits in the Real World: C...
- Bandit algorithms 6 - UCB - The Upper Confidence B...
- Bandit algorithms 5 - The Softmax Algorithm
- Bandit algorithms 4 - Debugging Bandit Algorithms
- Bandit algorithms 3 - The Epsilon-Greedy Algorithm
- Bandit algorithms 2 - Multiarmed Bandit Algorithms
- Bandit algorithms 1 - Exploration and Exploitation
- Python Data Analysis 11 - Recognizing Handwritten ...
- Python Data Analysis 10 - Embedding the JavaScript...
- Python Data Analysis 9 - An Example - Meteorologic...
- Python Data Analysis 8 - Machine Learning with sci...
- Python Data Analysis 7 - Data Visualization with m...
- Python Data Analysis 6 - pandas in Depth: Data Man...
-
▼
March
(25)
No comments:
Post a Comment