A Summary of lecture "Writing functions in Python", via datacamp
def print_return_type(func):
# Define wrapper(), the decorated function
def wrapper(*args, **kwargs):
# Call the function being decorated
result = func(*args, **kwargs)
print('{}() returned type {}'.format(func.__name__, type(result)))
return result
# Return the decorated function
return wrapper
@print_return_type
def foo(value):
return value
print(foo(42))
print(foo([1, 2, 3]))
print(foo({'a': 42}))
foo() returned type <class 'int'> 42 foo() returned type <class 'list'> [1, 2, 3] foo() returned type <class 'dict'> {'a': 42}
def counter(func):
def wrapper(*args, **kwargs):
wrapper.count += 1
# Call the function being decorated and return the result
return func()
wrapper.count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
foo()
foo()
print('foo() was called {} times.'.format(foo.count))
calling foo() calling foo() foo() was called 2 times.
def add_hello(func):
def wrapper(*args, **kwargs):
"""Print 'hello' and then call the decorated function."""
print('hello')
return func(*args, **kwargs)
return wrapper
# Decorate print_sum() with the add_hello() decorator
@add_hello
def print_sum(a, b):
"""Adds two numbers and prints the sum"""
print(a + b)
print_sum(10, 20)
print(print_sum.__doc__)
hello 30 Print 'hello' and then call the decorated function.
# Import the function to read docstring of decorator, not wrapper
from functools import wraps
def add_hello(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Print 'hello' and then call the decorated function."""
print('hello')
return func(*args, **kwargs)
return wrapper
# Decorate print_sum() with the add_hello() decorator
@add_hello
def print_sum(a, b):
"""Adds two numbers and prints the sum"""
print(a + b)
print_sum(10, 20)
print(print_sum.__doc__)
hello 30 Adds two numbers and prints the sum
import numpy as np
def check_inputs(a, *args, **kwargs):
for value in a:
time.sleep(0.01)
print('Finished checking inputs')
def check_outputs(a, *args, **kwargs):
for value in a:
time.sleep(0.01)
print('Finished checking outputs')
def check_everything(func):
@wraps(func)
def wrapper(*args, **kwargs):
check_inputs(*args, **kwargs)
result = func(*args, **kwargs)
check_outputs(result)
return result
return wrapper
import time
@check_everything
def duplicate(my_list):
"""Return a new list that repeats the input twice"""
return my_list + my_list
t_start = time.time()
duplicated_list = duplicate(list(range(50)))
t_end = time.time()
decorated_time = t_end - t_start
t_start = time.time()
# Call the original function instead of the decorated one
duplicated_list = duplicate.__wrapped__(list(range(50)))
t_end = time.time()
undecorated_time = t_end - t_start
print('Decorated time: {:.5f}s'.format(decorated_time))
print('Undecorated time: {:.5f}s'.format(undecorated_time))
Finished checking inputs Finished checking outputs Decorated time: 1.51825s Undecorated time: 0.00025s
def run_n_times(n):
"""Define and return a decorator"""
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@run_n_times(3)
def print_sum(a, b):
print(a + b)
print_sum(10, 5)
15 15 15
run_three_times = run_n_times(3)
@run_three_times
def print_sum(a, b):
print(a + b)
print_sum(20, 30)
50 50 50
def run_n_times(n):
"""Define and return a decorator"""
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
func(*args, **kwargs)
return wrapper
return decorator
# Make print_sum() run 10 times with the run_n_times() decorator
@run_n_times(10)
def print_sum(a, b):
print(a + b)
print_sum(15, 20)
35 35 35 35 35 35 35 35 35 35
# Use run_n_times() to create the run_five_times() decorator
run_five_times = run_n_times(5)
@run_five_times
def print_sum(a, b):
print(a + b)
print_sum(4, 100)
104 104 104 104 104
def bold(func):
@wraps(func)
def wrapper(*args, **kwargs):
msg = func(*args, **kwargs)
return '<b>{}</b>'.format(msg)
return wrapper
def italics(func):
@wraps(func)
def wrapper(*args, **kwargs):
msg = func(*args, **kwargs)
return '<i>{}</i>'.format(msg)
return wrapper
def html(open_tag, close_tag):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
msg = func(*args, **kwargs)
return '{}{}{}'.format(open_tag, msg, close_tag)
# Return the decorated function
return wrapper
# Return the decorator
return decorator
# Make hello() return bolded text
@html('<b>', '</b>')
def hello(name):
return 'Hello {}!'.format(name)
print(hello('Alice'))
<b>Hello Alice!</b>
# Make goodbye() return italicized text
@html('<i>', '</i>')
def goodbye(name):
return 'Goodbye {}.'.format(name)
print(goodbye('Alice'))
<i>Goodbye Alice.</i>
# Wrap the result of hello_goodbye() in <div> and </div>
@html('<div>', '</div>')
def hello_goodbye(name):
return '\n{}\n{}\n'.format(hello(name), goodbye(name))
print(hello_goodbye('Alice'))
<div> <b>Hello Alice!</b> <i>Goodbye Alice.</i> </div>
import signal
def raise_timeout(*args, **kwargs):
raise TimeoutError()
# When an 'alarm' signal goes off, call raise_timeout()
signal.signal(signalnum=signal.SIGALRM, handler=raise_timeout)
# Set off an alarm in 5 seconds
signal.alarm(5)
# Cancel the alarm
signal.alarm(0)
5
def timeout_in_5s(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Set an alarm for 5 seconds
signal.alarm(5)
try:
# Call the decorated func
return func(*args, **kwargs)
finally:
# Cancel alarm
signal.alarm(0)
return wrapper
@timeout_in_5s
def foo():
time.sleep(10)
print('foo!')
foo()
--------------------------------------------------------------------------- TimeoutError Traceback (most recent call last) <ipython-input-35-c19b6d9633cf> in <module> ----> 1 foo() <ipython-input-33-ae09c017bfbd> in wrapper(*args, **kwargs) 6 try: 7 # Call the decorated func ----> 8 return func(*args, **kwargs) 9 finally: 10 # Cancel alarm <ipython-input-34-79d4e0bed7e7> in foo() 1 @timeout_in_5s 2 def foo(): ----> 3 time.sleep(10) 4 print('foo!') <ipython-input-32-ea109267a28d> in raise_timeout(*args, **kwargs) 1 import signal 2 def raise_timeout(*args, **kwargs): ----> 3 raise TimeoutError() 4 5 # When an 'alarm' signal goes off, call raise_timeout() TimeoutError:
def timeout(n_seconds):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Set an alarm for n seconds
signal.alarm(n_seconds)
try:
return func(*args, **kwargs)
finally:
signal.alarm(0)
return wrapper
return decorator
@timeout(20)
def bar():
time.sleep(10)
print('bar!')
bar()
bar!
def tag(*tags):
# Define a new decorator, named "decorator", to return
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Call the function being decorated and return the result
return func(*args, **kwargs)
wrapper.tags = tags
return wrapper
# Return the new decorator
return decorator
@tag('test', 'this is a tag')
def foo():
pass
print(foo.tags)
('test', 'this is a tag')
def returns_dict(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
assert(type(result) == dict)
return result
return wrapper
@returns_dict
def foo(value):
return value
try:
print(foo([1, 2, 3]))
except AssertionError:
print('foo() did not return a dict')
foo() did not return a dict
def returns(return_type):
# Complete the returns() decorator
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
assert(type(result) == return_type)
return result
return wrapper
return decorator
@returns(dict)
def foo(value):
return value
try:
print(foo([1,2,3]))
except AssertionError:
print('foo() did not return a dict!')
foo() did not return a dict!