-
Notifications
You must be signed in to change notification settings - Fork 0
Decorators
A decorator is any function that accepts a function and returns a function.
Decorators are one of the main ways that Python implements functional programming principles.
Functions are first-class objects and can be passed as parameters.
import logging
def hello_wrapper(name, func):
func(f'Hello {name}')
hello_wrapper("world", func=print) # Hello world
hello_wrapper("logs", func=logging.warning) # WARNING:root:Hello logswith open('hello.txt', 'w') as f:
hello_wrapper('everyone!', func=f.write)import random
def anagram(t):
l = [c for c in t]
random.shuffle(l)
print("".join(l))
hello_wrapper('Japushku', anagram) # eHoulhluaskpJThe function has to be passed as a reference, actually calling it will cause the wrapper function to attempt to execute the value returned by the inner function.
hello_wrapper("world", func=print()) # Errordef outer():
print('Hi from the outer function')
def inner():
print('Hello from the inner function')
inner()We can use the __name__ attribute to access a passed function's name.
def hello(func):
print(f'Hello {func.__name__}')
hello(outer) # Hello outerWe can also return functions, which can then be invoked
def hello(func):
print(f'Hello {func.__name__}')
return func
hello(outer)()
'''
Hi from the outer function
Hello from the inner function
'''
new_outer = hello(outer)
new_outer is outer # Truedef wrapper(func):
print(f'Before {func.__name__}')
func()
print(f'After {func.__name__}')
wrapper(outer)
'''
Before outer
Hi from the outer function
Hello from the inner function
After outer
'''The true decorator pattern appears here, where wrapper is called the decorator and outer has been decorated.
def wrapper(func):
def _wrapper():
print(f'Before {func.__name__}')
func()
print(f'After {func.__name__}')
return _wrapper
outer = wrapper(outer)But the usual syntax since Python 2.4 is to place the decorator on the line above the decorated function, preceded by @:
@wrapper
def outer():
print('Hi from the outer function')
def inner():
print('Hello from the inner function')
inner()_wrapper here does not accept any positional arguments, so wrapping functions that take arguments will produce a TypeError
@wrapper
def say_hello(name):
print(f'Hello {name}!') # errorThe solution is to incorporate *args, **kwargs into the definition of the inner function, as well as the invocation of the function passed in.
def wrapper(func):
def _wrapper(*args, **kwargs):
print(f'Before {func.__name__}')
func(*args, **kwargs)
print(f'After {func.__name__}')
return _wrapperReturned values are not captured yet:
def wrapper(func):
def _wrapper(*args, **kwargs):
print(f'Before {func.__name__}')
value = func(*args, **kwargs)
print(f'After {func.__name__}')
return value
return _wrapperInspecting the decorated function's __name__ attribute reveals that it is still named _wrapper
say_hello.__name__ # '_wrapper'This is also true for other attributes, including docstring. functools.wraps is a decorator factory to reassign attributes to the wrapped function. This is considered superior to the functools.update_wrapper function which is also available.
def wrapper(func):
@functools.wraps(func)
def _wrapper(*args, **kwargs):
print(f'Before {func.__name__}')
value = func(*args, **kwargs)
print(f'After {func.__name__}')
return value
return _wrapperThis forms an ideal starting template for the creation of custom decorators.
- argparse ?
- array ?
- asyncio ?
- bisect ?
- csv ?
- ctypes ?
- curses ?
- datetime ?
- functools ?
- getpass ?
- glob ?
- heapq ?
- http ?
- json ?
- logging ?
- optparse ?
- os ?
- pathlib ?
- platform ?
- pythonnet ?
- random ?
- socket ?
- subprocess ?
- sqlite3 ?
- sys ?
- termcolor ?
- threading ?
- trace ?
- typing ?
- unittest ?
- urllib ?
- venv ?
- weakref ?
- winrm ?