Remember function transformations where a (higher-order) function takes a function and returns a function with new behavior? Python decorators are just syntactic sugar around that. "Syntactic sugar" just means "a more convenient syntax".
Example:
def vowel_counter(func_to_decorate):
vowel_count = 0
def wrapper(doc):
nonlocal vowel_count
vowels = "aeiou"
for char in doc:
if char in vowels:
vowel_count += 1
print(f"Vowel count: {vowel_count}")
return func_to_decorate(doc)
return wrapper
@vowel_counter
def process_doc(doc):
print(f"Document: {doc}")
process_doc("What")
# Vowel count: 1
# Document: What
process_doc("a wonderful")
# Vowel count: 5
# Document: a wonderful
process_doc("world")
# Vowel count: 6
# Document: world
The @vowel_counter line is "decorating" the process_doc function with the vowel_counter function. vowel_counter is called once when process_doc is defined with the @ syntax, but the wrapper function that it returns is called every time process_doc is called. That's why vowel_count is preserved and printed after each time.
Python decorators are just another (sometimes simpler) way of writing a higher-order function. These two pieces of code are identical:
@vowel_counter
def process_doc(doc):
print(f"Document: {doc}")
process_doc("Something wicked this way comes")
def process_doc(doc):
print(f"Document: {doc}")
process_doc = vowel_counter(process_doc)
process_doc("Something wicked this way comes")
The provided file_type_aggregator function is intended to decorate other functions. It assumes that the function it decorates has exactly 2 positional arguments.
Create a process_doc function that's decorated by file_type_aggregator. It should return the following string:
f"Processing doc: '{doc}'. File Type: {file_type}"
Where doc and file_type are its positional arguments. (See line 11 for where it's being called)